diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/clock.c | 99 | ||||
| -rw-r--r-- | src/device.c | 277 | ||||
| -rw-r--r-- | src/idle.c | 112 | ||||
| -rw-r--r-- | src/ipc.c | 2200 | ||||
| -rw-r--r-- | src/irq.c | 69 | ||||
| -rw-r--r-- | src/kservice.c | 922 | ||||
| -rw-r--r-- | src/kservice.h | 100 | ||||
| -rw-r--r-- | src/mem.c | 504 | ||||
| -rw-r--r-- | src/mempool.c | 410 | ||||
| -rw-r--r-- | src/object.c | 401 | ||||
| -rw-r--r-- | src/scheduler.c | 414 | ||||
| -rw-r--r-- | src/slab.c | 815 | ||||
| -rw-r--r-- | src/thread.c | 625 | ||||
| -rw-r--r-- | src/timer.c | 384 |
14 files changed, 7332 insertions, 0 deletions
diff --git a/src/clock.c b/src/clock.c new file mode 100644 index 0000000000..b45ca8d24f --- /dev/null +++ b/src/clock.c @@ -0,0 +1,99 @@ +/* + * File : clock.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-12 Bernard first version + * 2006-05-27 Bernard add support for same priority thread schedule + * 2006-08-10 Bernard remove the last rt_schedule in rt_tick_increase + */ + +#include <rtthread.h> + +static rt_tick_t rt_tick; +static rt_time_t rt_passed_second; + +extern void rt_timer_check(void); + +/** + * This function will init system tick and set it to zero. + * @ingroup SystemInit + * + */ +void rt_system_tick_init() +{ + rt_tick = 0; + rt_passed_second = 0; +} + +/** + * @addtogroup Clock + */ + +/*@{*/ + +/** + * This function will return current tick from operating system startup + * + * @return current tick + */ +rt_tick_t rt_tick_get() +{ + /* return the global tick */ + return rt_tick; +} + +/** + * This function will notify kernel there is one tick passed. Normally, + * this function is invoked by clock ISR. + */ +void rt_tick_increase() +{ + struct rt_thread* thread; + + /* increase the global tick */ + ++ rt_tick; + + if (rt_tick % RT_TICK_PER_SECOND == 0) + { + ++rt_passed_second; + } + + /* check timer */ + rt_timer_check(); + + /* check time slice */ + thread = rt_thread_self(); + + -- thread->remaining_tick; + if (thread->remaining_tick == 0) + { + /* change to initialized tick */ + thread->remaining_tick = thread->init_tick; + + /* yield */ + rt_thread_yield(); + } +} + +/** + * This function will calculate the tick from millisecond. + * + * @param ms the specified millisecond + * + * @return the calculated tick + */ +rt_tick_t rt_tick_from_millisecond(rt_uint32_t ms) +{ + /* return the calculated tick */ + return RT_TICK_PER_SECOND * (ms / 1000); +} + +/*@}*/ + diff --git a/src/device.c b/src/device.c new file mode 100644 index 0000000000..cbd42cf103 --- /dev/null +++ b/src/device.c @@ -0,0 +1,277 @@ +/* + * File : device.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2007-01-21 Bernard the first version + */ + +#include <rtthread.h> +#include "kservice.h" + +#ifdef RT_USING_DEVICE + +/** + * This function registers a device driver with specified name. + * + * @param dev the pointer of device driver structure + * @param name the device driver's name + * @param flags the flag of device + * + * @return the error code, RT_EOK on initialization successfully. + */ +rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint16_t flags) +{ + if (dev == RT_NULL) return -RT_ERROR; + + rt_object_init(&(dev->parent), RT_Object_Class_Device, name); + dev->flag = flags; + + return RT_EOK; +} + +/** + * This function removes a previouly registered device driver + * + * @param dev the pointer of device driver structure + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_device_unregister(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + + rt_object_detach(&(dev->parent)); + + return RT_EOK; +} + +/** + * This function initializes all registered device driver + * + * @return the error code, RT_EOK on successfully. + */ +rt_err_t rt_device_init_all() +{ + struct rt_device* device; + struct rt_list_node* node; + struct rt_object_information *information; + register rt_err_t result; + + extern struct rt_object_information rt_object_container[]; + + information = &rt_object_container[RT_Object_Class_Device]; + + /* for each device */ + for (node = information->object_list.next; node != &(information->object_list); node = node->next) + { + rt_err_t (*init)(rt_device_t dev); + device = (struct rt_device*)rt_list_entry(node, struct rt_object, list); + + /* get device init handler */ + init = device->init; + if (init != RT_NULL && !(device->flag & RT_DEVICE_FLAG_ACTIVATED)) + { + result = init(device); + if (result != RT_EOK) + { + rt_kprintf("init device:%s error:\n", device->parent.name); + rt_kprintf("error code:%d\n", result); + } + } + } + + return RT_EOK; +} + +/** + * This function finds a device driver by specified name. + * + * @param name the device driver's name + * + * @return the registered device driver on successful, or RT_NULL on failure. + */ +rt_device_t rt_device_find(const char* name) +{ + /* try to find device object */ + return (struct rt_device*) rt_object_find (RT_Object_Class_Device, + name); +} + +/** + * This function will open a device + * + * @param dev the pointer of device driver structure + * + * @return the result + */ +rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) +{ + rt_err_t result; + rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag); + + RT_ASSERT(dev != RT_NULL); + + result = RT_EOK; + + /* device is a standalone device and opened */ + if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) && + (dev->open_flag & RT_DEVICE_OFLAG_OPEN)) + return -RT_EBUSY; + + /* call device open interface */ + open = dev->open; + if (open != RT_NULL) + { + result = open(dev, oflag); + } + else + { + /* no this interface in device driver */ + result = -RT_ENOSYS; + } + + /* set open flag */ + if (result == RT_EOK || result == -RT_ENOSYS) + dev->open_flag = oflag | RT_DEVICE_OFLAG_OPEN; + + return result; +} + +/** + * This function will close a device + * + * @param dev the pointer of device driver structure + * + * @return the result + */ +rt_err_t rt_device_close(rt_device_t dev) +{ + rt_err_t result; + rt_err_t (*close)(rt_device_t dev); + + RT_ASSERT(dev != RT_NULL); + + /* call device close interface */ + close = dev->close; + if (close != RT_NULL) + { + result = close(dev); + } + else + { + /* no this interface in device driver */ + result = -RT_ENOSYS; + } + + /* set open flag */ + if (result == RT_EOK || result == -RT_ENOSYS) + dev->open_flag = RT_DEVICE_OFLAG_CLOSE; + + return result; +} + +/** + * This function will read some data from a device. + * + * @param dev the pointer of device driver structure + * @param pos the position of reading + * @param buffer the data buffer to save read data + * @param size the size of buffer + * + * @return the actually read size on successful, otherwise negative returned. + */ +rt_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) +{ + rt_size_t (*read)(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size); + + RT_ASSERT(dev != RT_NULL); + + /* call device read interface */ + read = dev->read; + if (read != RT_NULL) + { + return read(dev, pos, buffer, size); + } + + /* set error code */ + rt_set_errno(-RT_ENOSYS); + return 0; +} + +/** + * This function will write some data to a device. + * + * @param dev the pointer of device driver structure + * @param pos the position of written + * @param buffer the data buffer to be written to device + * @param size the size of buffer + * + * @return the actually written size on successful, otherwise negative returned. + */ +rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) +{ + rt_size_t (*write)(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size); + + RT_ASSERT(dev != RT_NULL); + + /* call device write interface */ + write = dev->write; + if (write != RT_NULL) + { + return write(dev, pos, buffer, size); + } + + /* set error code */ + rt_set_errno(-RT_ENOSYS); + return 0; +} + +/** + * This function will perform a variety of control functions on devices. + * + * @param dev the pointer of device driver structure + * @param cmd the command sent to device + * @param arg the argument of command + * + * @return the result + */ +rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg) +{ + rt_err_t (*control)(rt_device_t dev, rt_uint8_t cmd, void* arg); + + RT_ASSERT(dev != RT_NULL); + + /* call device write interface */ + control = dev->control; + if (control != RT_NULL) + { + return control(dev, cmd, arg); + } + + return -RT_ENOSYS; +} + +rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind )(rt_device_t dev, rt_size_t size)) +{ + RT_ASSERT(dev != RT_NULL); + + dev->rx_indicate = rx_ind; + return RT_EOK; +} + +rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev, void *buffer)) +{ + RT_ASSERT(dev != RT_NULL); + + dev->tx_complete = tx_done; + return RT_EOK; +} + +#endif diff --git a/src/idle.c b/src/idle.c new file mode 100644 index 0000000000..f7da5e2c81 --- /dev/null +++ b/src/idle.c @@ -0,0 +1,112 @@ +/* + * File : idle.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-23 Bernard the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include "kservice.h" + +#if defined (RT_USING_HOOK) || defined(RT_USING_HEAP) +#define IDLE_THREAD_STACK_SIZE 256 +#else +#define IDLE_THREAD_STACK_SIZE 128 +#endif + +static struct rt_thread idle; +static rt_uint8_t rt_thread_stack[IDLE_THREAD_STACK_SIZE]; + +#ifdef RT_USING_HEAP +extern rt_list_t rt_thread_defunct; +#endif + +#ifdef RT_USING_HOOK +/** + * @addtogroup Hook + */ +/*@{*/ + +static void (*rt_thread_idle_hook)(); + +/** + * This function will set a hook function to idle thread loop. + * + * @param hook the specified hook function + * + * @note the hook function must be simple and never be blocked or suspend. + */ +void rt_thread_idle_sethook(void (*hook)()) +{ + rt_thread_idle_hook = hook; +} +/*@}*/ +#endif + +static void rt_thread_idle_entry(void* parameter) +{ + while (1) + { +#ifdef RT_USING_HOOK + /* if there is an idle thread hook */ + if (rt_thread_idle_hook != RT_NULL) rt_thread_idle_hook(); +#endif + +#ifdef RT_USING_HEAP + /* check the defunct thread list */ + if (!rt_list_isempty(&rt_thread_defunct)) + { + rt_base_t lock; + + struct rt_thread* thread = rt_list_entry(rt_thread_defunct.next, struct rt_thread, tlist); + + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); + + rt_list_remove(&(thread->tlist)); + + /* enable interrupt */ + rt_hw_interrupt_enable(lock); + + /* release thread's stack */ + rt_free(thread->stack_addr); + + /* delete thread object */ + rt_object_delete((rt_object_t)thread); + } +#endif + } +} + +/** + * @addtogroup SystemInit + */ +/*@{*/ + +/** + * This function will initialize idle thread, then start it. + * + * @note this function must be invoked when system init. + */ +void rt_thread_idle_init() +{ + /* init thread */ + rt_thread_init(&idle, + "tidle", + rt_thread_idle_entry, RT_NULL, + &rt_thread_stack[0], sizeof(rt_thread_stack), + RT_THREAD_PRIORITY_MAX - 1, 32); + + /* startup */ + rt_thread_startup(&idle); +} + +/*@}*/ diff --git a/src/ipc.c b/src/ipc.c new file mode 100644 index 0000000000..b5d7cd3b86 --- /dev/null +++ b/src/ipc.c @@ -0,0 +1,2200 @@ +/* + * File : ipc.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-14 Bernard the first version + * 2006-04-25 Bernard implement semaphore + * 2006-05-03 Bernard add IPC_DEBUG + * modify the type of IPC waiting time to rt_int32_t + * 2006-05-10 Bernard fix the semaphore take bug and add IPC object + * 2006-05-12 Bernard implement mailbox and message queue + * 2006-05-20 Bernard implement mutex + * 2006-05-23 Bernard implement fast event + * 2006-05-24 Bernard implement event + * 2006-06-03 Bernard fix the thread timer init bug + * 2006-06-05 Bernard fix the mutex release bug + * 2006-06-07 Bernard fix the message queue send bug + * 2006-08-04 Bernard add hook support + * 2009-05-21 Yi.qiu fix the sem release bug + */ + +#include <rtthread.h> +#include <rthw.h> + +#include "kservice.h" + +/* #define IPC_DEBUG */ + +#ifdef RT_USING_HOOK +extern void (*rt_object_trytake_hook)(struct rt_object* object); +extern void (*rt_object_take_hook)(struct rt_object* object); +extern void (*rt_object_put_hook)(struct rt_object* object); +#endif + +/** + * @addtogroup IPC + */ + +/*@{*/ + +/** + * This function will initialize an IPC object + * + * @param ipc the IPC object + * + * @return the operation status, RT_EOK on successful + */ +rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc) +{ + /* init ipc object */ + rt_list_init(&(ipc->suspend_thread)); + ipc->suspend_thread_count = 0; + + return RT_EOK; +} + +/** + * This function will suspend a thread for a specified IPC object and put the + * thread into suspend queue of IPC object + * + * @param ipc the IPC object + * @param thread the thread object to be suspended + * + * @return the operation status, RT_EOK on successful + */ +rt_inline rt_err_t rt_ipc_object_suspend(struct rt_ipc_object *ipc, struct rt_thread *thread) +{ + /* suspend thread */ + rt_thread_suspend(thread); + ipc->suspend_thread_count ++; + + switch (ipc->parent.flag) + { + case RT_IPC_FLAG_FIFO: + rt_list_insert_before(&(ipc->suspend_thread), &(thread->tlist)); + break; + + case RT_IPC_FLAG_PRIO: + { + struct rt_list_node* n; + struct rt_thread* sthread; + + /* find a suitable position */ + for (n = ipc->suspend_thread.next; n != &(ipc->suspend_thread); + n = n->next) + { + sthread = rt_list_entry(n, struct rt_thread, tlist); + + /* find out */ + if (thread->current_priority < sthread->current_priority) break; + } + + rt_list_insert_before(&(ipc->suspend_thread), &(thread->tlist)); + } + break; + } + + return RT_EOK; +} + +/** + * This function will resume a thread from an IPC object: + * - remove the thread from suspend queue of IPC object + * - put the thread into system ready queue + * + * @param ipc the IPC object + * + * @return the operation status, RT_EOK on successful + */ +rt_inline rt_err_t rt_ipc_object_resume(struct rt_ipc_object* ipc) +{ + struct rt_thread *thread; + + /* get thread entry */ + thread = rt_list_entry(ipc->suspend_thread.next, struct rt_thread, tlist); + +#ifdef IPC_DEBUG + rt_kprintf("resume thread:%s\n", thread->name); +#endif + + /* resume it */ + rt_thread_resume(thread); + + /* decrease suspended thread count */ + ipc->suspend_thread_count --; + + return RT_EOK; +} + +/** + * This function will resume all suspended threads in an IPC object. + * + * @param ipc the IPC object + * + * @return the operation status, RT_EOK on successful + */ +rt_inline rt_err_t rt_ipc_object_resume_all(struct rt_ipc_object* ipc) +{ + struct rt_thread* thread; + register rt_ubase_t temp; + + /* wakeup all suspend threads */ + while (!rt_list_isempty(&(ipc->suspend_thread))) + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(ipc->suspend_thread.next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* + * resume thread + * In rt_thread_resume function, it will remove current thread from + * suspend list + */ + rt_thread_resume(thread); + + /* decrease suspended thread count */ + ipc->suspend_thread_count --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } + + return RT_EOK; +} + +/* decrease ipc suspended thread number when thread can not take resource successfully */ +rt_inline void rt_ipc_object_decrease(struct rt_ipc_object* ipc) +{ + register rt_ubase_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* decrease suspended thread count */ + ipc->suspend_thread_count --; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} +#ifdef RT_USING_SEMAPHORE + +/** + * This function will initialize a semaphore and put it under control of resource + * management. + * + * @param sem the semaphore object + * @param name the name of semaphore + * @param value the init value of semaphore + * @param flag the flag of semaphore + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_sem_init (rt_sem_t sem, const char* name, rt_uint32_t value, rt_uint8_t flag) +{ + RT_ASSERT(sem != RT_NULL); + + /* init object */ + rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name); + + /* init ipc object */ + rt_ipc_object_init(&(sem->parent)); + + /* set init value */ + sem->value = value; + + /* set parent */ + sem->parent.parent.flag = flag; + + return RT_EOK; +} + +/** + * This function will detach a semaphore from resource management + * + * @param sem the semaphore object + * + * @return the operation status, RT_EOK on successful + * + * @see rt_sem_delete + */ +rt_err_t rt_sem_detach (rt_sem_t sem) +{ + RT_ASSERT(sem != RT_NULL); + + /* wakeup all suspend threads */ + rt_ipc_object_resume_all(&(sem->parent)); + + /* detach semaphore object */ + rt_object_detach(&(sem->parent.parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a semaphore from system resource + * + * @param name the name of semaphore + * @param value the init value of semaphore + * @param flag the flag of semaphore + * + * @return the created semaphore, RT_NULL on error happen + * + * @see rt_sem_init + */ +rt_sem_t rt_sem_create (const char* name, rt_uint32_t value, rt_uint8_t flag) +{ + rt_sem_t sem; + + /* allocate object */ + sem = (rt_sem_t) rt_object_allocate(RT_Object_Class_Semaphore, name); + if (sem == RT_NULL) return sem; + + /* init ipc object */ + rt_ipc_object_init(&(sem->parent)); + + /* set init value */ + sem->value = value; + + /* set parent */ + sem->parent.parent.flag = flag; + + return sem; +} + +/** + * This function will delete a semaphore object and release the memory + * + * @param sem the semaphore object + * + * @return the error code + * + * @see rt_sem_detach + */ +rt_err_t rt_sem_delete (rt_sem_t sem) +{ + RT_ASSERT(sem != RT_NULL); + + /* wakeup all suspend threads */ + rt_ipc_object_resume_all(&(sem->parent)); + + /* delete semaphore object */ + rt_object_delete(&(sem->parent.parent)); + + return RT_EOK; +} +#endif + +/** + * This function will take a semaphore, if the semaphore is unavailable, the + * thread shall wait for a specified time. + * + * @param sem the semaphore object + * @param time the waiting time + * + * @return the error code + */ +rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time) +{ + register rt_base_t temp; + struct rt_thread* thread; + + RT_ASSERT(sem != RT_NULL); + +#ifdef RT_USING_HOOK + if (rt_object_trytake_hook != RT_NULL) rt_object_trytake_hook(&(sem->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef IPC_DEBUG + rt_kprintf("thread %s take sem:%s, which value is: %d\n", rt_thread_self()->name, + ((struct rt_object*)sem)->name, sem->value); +#endif + if (sem->value > 0) + { + /* semaphore is available */ + sem->value --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } + else + { + /* no waiting, return with timeout */ + if (time == 0 ) + { + rt_hw_interrupt_enable(temp); + return -RT_ETIMEOUT; + } + else + { + /* semaphore is unavailable, push to suspend list */ + sem->value --; + + /* get current thread */ + thread = rt_thread_self(); + + /* reset thread error number */ + thread->error = RT_EOK; + +#ifdef IPC_DEBUG + rt_kprintf("sem take: suspend thread - %s\n", thread->name); +#endif + + /* suspend thread */ + rt_ipc_object_suspend(&(sem->parent), thread); + + /* has waiting time, start thread timer */ + if (time > 0) + { +#ifdef IPC_DEBUG + rt_kprintf("set thread:%s to timer list\n", thread->name); +#endif + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &time); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* do schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + { + /* decrease suspended thread count */ + rt_ipc_object_decrease(&(sem->parent)); + return thread->error; + } + } + } + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(sem->parent.parent)); +#endif + + return RT_EOK; +} + +/** + * This function will try to take a semaphore and immediately return + * + * @param sem the semaphore object + * + * @return the error code + */ +rt_err_t rt_sem_trytake(rt_sem_t sem) +{ + return rt_sem_take(sem, 0); +} + +/** + * This function will release a semaphore, if there are threads suspended on + * semaphore, it will be waked up. + * + * @param sem the semaphore object + * + * @return the error code + */ +rt_err_t rt_sem_release(rt_sem_t sem) +{ + register rt_base_t temp; + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(sem->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef IPC_DEBUG + rt_kprintf("thread %s releases sem:%s, which value is: %d\n", rt_thread_self()->name, + ((struct rt_object*)sem)->name, sem->value); +#endif + /* increase value */ + sem->value ++; + + if (sem->value <= 0 && sem->parent.suspend_thread_count > 0) + { + rt_ipc_object_resume(&(sem->parent)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* resume a thread, re-schedule */ + rt_schedule(); + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +/** + * This function can get or set some extra attributions of a semaphore object. + * + * @param sem the semaphore object + * @param cmd the execution command + * @param arg the execution argument + * + * @return the error code + */ +rt_err_t rt_sem_control(rt_sem_t sem, rt_uint8_t cmd, void* arg) +{ + return RT_EOK; +} + +#endif /* end of RT_USING_SEMAPHORE */ + +#ifdef RT_USING_MUTEX + +/** + * This function will initialize a mutex and put it under control of resource + * management. + * + * @param mutex the mutex object + * @param name the name of mutex + * @param flag the flag of mutex + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_mutex_init (rt_mutex_t mutex, const char* name, rt_uint8_t flag) +{ + RT_ASSERT(mutex != RT_NULL); + + /* init object */ + rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name); + + /* init ipc object */ + rt_ipc_object_init(&(mutex->parent)); + + mutex->value = 1; + mutex->owner = RT_NULL; + mutex->original_priority = 0xFF; + mutex->hold = 0; + + /* set flag */ + mutex->parent.parent.flag = flag; + + return RT_EOK; +} + +/** + * This function will detach a mutex from resource management + * + * @param mutex the mutex object + * + * @return the operation status, RT_EOK on successful + * + * @see rt_mutex_delete + */ +rt_err_t rt_mutex_detach (rt_mutex_t mutex) +{ + RT_ASSERT(mutex != RT_NULL); + + /* wakeup all suspend threads */ + rt_ipc_object_resume_all(&(mutex->parent)); + + /* detach semaphore object */ + rt_object_detach(&(mutex->parent.parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a mutex from system resource + * + * @param name the name of mutex + * @param flag the flag of mutex + * + * @return the created mutex, RT_NULL on error happen + * + * @see rt_mutex_init + */ +rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag) +{ + struct rt_mutex *mutex; + + /* allocate object */ + mutex = (rt_mutex_t) rt_object_allocate(RT_Object_Class_Mutex, name); + if (mutex == RT_NULL) return mutex; + + /* init ipc object */ + rt_ipc_object_init(&(mutex->parent)); + + mutex->value = 1; + mutex->owner = RT_NULL; + mutex->original_priority = 0xFF; + mutex->hold = 0; + + /* set flag */ + mutex->parent.parent.flag = flag; + + return mutex; +} + +/** + * This function will delete a mutex object and release the memory + * + * @param mutex the mutex object + * + * @return the error code + * + * @see rt_mutex_detach + */ +rt_err_t rt_mutex_delete (rt_mutex_t mutex) +{ + RT_ASSERT(mutex != RT_NULL); + + /* wakeup all suspend threads */ + rt_ipc_object_resume_all(&(mutex->parent)); + + /* delete semaphore object */ + rt_object_delete(&(mutex->parent.parent)); + + return RT_EOK; +} +#endif + +/** + * This function will take a mutex, if the mutex is unavailable, the + * thread shall wait for a specified time. + * + * @param mutex the mutex object + * @param time the waiting time + * + * @return the error code + */ +rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time) +{ + register rt_base_t temp; + struct rt_thread* thread; + + RT_ASSERT(mutex != RT_NULL); + +#ifdef RT_USING_HOOK + if (rt_object_trytake_hook != RT_NULL) rt_object_trytake_hook(&(mutex->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef IPC_DEBUG + rt_kprintf("mutex_take:mutex value: %d, hold: %d\n", mutex->value, mutex->hold); +#endif + + /* get current thread */ + thread = rt_thread_self(); + + /* reset thread error */ + thread->error = RT_EOK; + + if (mutex->owner == thread) + { + /* it's the same thread */ + mutex->hold ++; + } + else + { + if (mutex->value > 0) + { + /* mutex is available */ + mutex->value --; + + /* set mutex owner and original priority */ + mutex->owner = thread; + mutex->original_priority = thread->current_priority; + mutex->hold ++; + } + else + { + /* no waiting, return with timeout */ + if (time == 0 ) + { + /* set error as timeout */ + thread->error = -RT_ETIMEOUT; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_ETIMEOUT; + } + else + { + /* mutex is unavailable, push to suspend list */ + mutex->value --; + +#ifdef IPC_DEBUG + rt_kprintf("sem take: suspend thread: %s\n", thread->name); +#endif + /* change the owner thread priority of mutex */ + if (thread->current_priority < mutex->owner->current_priority) + { + /* change the owner thread priority */ + rt_thread_control(mutex->owner, RT_THREAD_CTRL_CHANGE_PRIORITY, + &thread->current_priority); + } + + /* suspend current thread */ + rt_ipc_object_suspend(&(mutex->parent), thread); + + /* has waiting time, start thread timer */ + if (time > 0) + { +#ifdef IPC_DEBUG + rt_kprintf("set thread:%s to timer list\n", thread->name); +#endif + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &time); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* do schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + { + /* decrease suspended thread count */ + rt_ipc_object_decrease(&(mutex->parent)); + + /* return error */ + return thread->error; + } + else + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* take mutex */ + mutex->owner = thread; + mutex->hold ++; + + /* set thread error */ + thread->error = RT_EOK; + } + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(mutex->parent.parent)); +#endif + + return RT_EOK; +} + +/** + * This function will release a mutex, if there are threads suspended on mutex, + * it will be waked up. + * + * @param mutex the mutex object + * + * @return the error code + */ +rt_err_t rt_mutex_release(rt_mutex_t mutex) +{ + register rt_base_t temp; + struct rt_thread* thread; + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef IPC_DEBUG + rt_kprintf("mutex_release:mutex value: %d, hold: %d\n", mutex->value, mutex->hold); +#endif + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(mutex->parent.parent)); +#endif + + /* get current thread */ + thread = rt_thread_self(); + + /* mutex is only released by owner */ + if (thread != mutex->owner) + { + thread->error = -RT_ERROR; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_ERROR; + } + + /* decrease hold */ + mutex->hold --; + + /* if no hold */ + if (mutex->hold == 0) + { + /* change the owner thread to original priority */ + if (mutex->owner->init_priority != mutex->owner->current_priority) + { + rt_thread_control(mutex->owner, RT_THREAD_CTRL_CHANGE_PRIORITY, + &(mutex->owner->init_priority)); + } + + /* wakeup suspended thread */ + if (mutex->value <= 0 && mutex->parent.suspend_thread_count > 0) + { +#ifdef IPC_DEBUG + rt_kprintf("mutex release: resume thread: %s\n", thread->name); +#endif + + /* resume thread */ + rt_ipc_object_resume(&(mutex->parent)); + } + + /* increase value */ + mutex->value ++; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +/** + * This function can get or set some extra attributions of a mutex object. + * + * @param mutex the mutex object + * @param cmd the execution command + * @param arg the execution argument + * + * @return the error code + */ +rt_err_t rt_mutex_control(rt_mutex_t mutex, rt_uint8_t cmd, void* arg) +{ + return RT_EOK; +} + +#endif /* end of RT_USING_MUTEX */ + +#ifdef RT_USING_FASTEVENT + +/** + * This function will initialize a fast event and put it under control of resource + * management. + * + * @param event the fast event object + * @param name the name of fast event + * @param flag the flag of fast event + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_fast_event_init(rt_fast_event_t event, const char* name, rt_uint8_t flag) +{ + register rt_base_t offset; + RT_ASSERT(event != RT_NULL); + + /* init object */ + rt_object_init(&(event->parent), RT_Object_Class_FastEvent, name); + + /* set parent */ + event->parent.flag = flag; + + /* clear event set */ + event->set = 0x00; + + /* init thread list */ + for (offset = 0; offset < 32; offset ++) + { + rt_list_init(&(event->thread_list[offset])); + } + + return RT_EOK; +} + +/** + * This function will detach a fast event from resource management + * + * @param event the fast event object + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_fast_event_detach(rt_fast_event_t event) +{ + register rt_base_t bit; + struct rt_thread* thread; + register rt_ubase_t level; + + RT_ASSERT(event != RT_NULL); + + for (bit = 0; bit < RT_EVENT_LENGTH; bit ++) + { + /* resume all suspend thread */ + if (!rt_list_isempty(&(event->thread_list[bit]))) + { + /* wakeup all suspend threads */ + while (!rt_list_isempty(&(event->thread_list[bit]))) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(event->thread_list[bit].next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* resume thread */ + rt_thread_resume(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + } + + } + } + + /* detach event object */ + rt_object_detach(&(event->parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a fast event object from system resource + * + * @param name the name of fast event + * @param flag the flag of fast event + * + * @return the created fast event, RT_NULL on error happen + */ +rt_fast_event_t rt_fast_event_create (const char* name, rt_uint8_t flag) +{ + rt_fast_event_t event; + register rt_base_t offset; + + /* allocate object */ + event = (rt_fast_event_t) rt_object_allocate(RT_Object_Class_FastEvent, name); + if (event == RT_NULL) return event; + + /* set parent */ + event->parent.flag = flag; + + /* clear event set */ + event->set = 0x00; + + /* init thread list */ + for (offset = 0; offset < 32; offset ++) + { + rt_list_init(&(event->thread_list[offset])); + } + + return event; +} + +/** + * This function will delete a fast event object and release the memory + * + * @param event the fast event object + * + * @return the error code + */ +rt_err_t rt_fast_event_delete (rt_fast_event_t event) +{ + register rt_base_t bit; + struct rt_thread* thread; + register rt_ubase_t level; + + RT_ASSERT(event != RT_NULL); + + for (bit = 0; bit < RT_EVENT_LENGTH; bit ++) + { + /* resume all suspend thread */ + if (!rt_list_isempty(&(event->thread_list[bit]))) + { + /* wakeup all suspend threads */ + while (!rt_list_isempty(&(event->thread_list[bit]))) + { + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(event->thread_list[bit].next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* resume thread */ + rt_thread_resume(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + } + + } + } + + /* detach semaphore object */ + rt_object_delete(&(event->parent)); + + return RT_EOK; +} +#endif + +/** + * This function will send an event to the fast event object, if there are threads + * suspended on fast event object, it will be waked up. + * + * @param event the fast event object + * @param bit the event bit + * + * @return the error code + */ +rt_err_t rt_fast_event_send(rt_fast_event_t event, rt_uint8_t bit) +{ + rt_uint32_t offset; + register rt_ubase_t level; + struct rt_thread *thread; + struct rt_list_node *n; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(bit < RT_EVENT_LENGTH); + + offset = 1 << bit; + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(event->parent)); +#endif + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + event->set |= offset; + + /* if thread list at offset is not empty */ + n = event->thread_list[bit].next; + while (n != &(event->thread_list[bit])) + { + /* get thread */ + thread = rt_list_entry(n, struct rt_thread, tlist); + + /* move to next node */ + n = n->next; + + /* clear bit or not */ + if (thread->event_info & RT_EVENT_FLAG_CLEAR) + event->set &= ~offset; + + /* resume thread */ + rt_thread_resume(thread); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + return RT_EOK; +} + +/** + * This function will receive an event from fast event object, if the event is + * unavailable, the thread shall wait for a specified time. + * + * @param event the fast event object + * @param bit the interested event + * @param option the receive option + * @param timeout the waiting time + * + * @return the error code + */ +rt_err_t rt_fast_event_recv(rt_fast_event_t event, rt_uint8_t bit, rt_uint8_t option, rt_int32_t timeout) +{ + rt_base_t offset; + struct rt_thread* thread; + register rt_ubase_t level; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + RT_ASSERT(bit < RT_EVENT_LENGTH); + + offset = 1 << bit; + +#ifdef RT_USING_HOOK + if (rt_object_trytake_hook != RT_NULL) rt_object_trytake_hook(&(event->parent)); +#endif + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* get current thread */ + thread = rt_thread_self(); + thread->error = RT_EOK; + + /* get event successfully */ + if (event->set & offset) + { + if (option & RT_EVENT_FLAG_CLEAR) + event->set &= ~ offset; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return RT_EOK; + } + + /* no event happen */ + + /* check waiting time */ + if (timeout == 0) + { + /* no waiting */ + thread->error = -RT_ETIMEOUT; + } + else + { + /* there are no event, suspend thread */ + rt_thread_suspend(thread); + + /* set event info in thread */ + thread->event_info = option; + + switch (event->parent.flag) + { + case RT_IPC_FLAG_FIFO: + rt_list_insert_after(&(event->thread_list[bit]), &(thread->tlist)); + break; + + case RT_IPC_FLAG_PRIO: + { + struct rt_list_node* n; + struct rt_thread* sthread; + + /* find a suitable position */ + for (n = event->thread_list[bit].next; n != &(event->thread_list[bit]); n = n->next) + { + sthread = rt_list_entry(n, struct rt_thread, tlist); + + /* find out */ + if (thread->current_priority < sthread->current_priority) break; + } + + /* insert thread */ + rt_list_insert_before(&(event->thread_list[bit]), &(thread->tlist)); + } + break; + } + + /* if there is timeout, active thread timer */ + if (timeout > 0) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); + rt_timer_start(&(thread->thread_timer)); + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(event->parent)); +#endif + + return thread->error; +} + +/** + * This function can get or set some extra attributions of a fast event object. + * + * @param event the event object + * @param cmd the execution command + * @param arg the execution argument + * + * @return the error code + */ +rt_err_t rt_fast_event_control (rt_fast_event_t event, rt_uint8_t cmd, void* arg) +{ + return RT_EOK; +} +#endif + +#ifdef RT_USING_EVENT + +/** + * This function will initialize an event and put it under control of resource + * management. + * + * @param event the event object + * @param name the name of event + * @param flag the flag of event + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_event_init(rt_event_t event, const char* name, rt_uint8_t flag) +{ + RT_ASSERT(event != RT_NULL); + + /* init object */ + rt_object_init(&(event->parent.parent), RT_Object_Class_Event, name); + + /* set parent flag */ + event->parent.parent.flag = flag; + + /* init ipc object */ + rt_ipc_object_init(&(event->parent)); + + /* init event */ + event->set = 0; + + return RT_EOK; +} + +/** + * This function will detach an event object from resource management + * + * @param event the event object + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_event_detach(rt_event_t event) +{ + /* parameter check */ + RT_ASSERT(event != RT_NULL); + + /* resume all suspended thread */ + rt_ipc_object_resume_all(&(event->parent)); + + /* detach event object */ + rt_object_detach(&(event->parent.parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create an event object from system resource + * + * @param name the name of event + * @param flag the flag of event + * + * @return the created event, RT_NULL on error happen + */ +rt_event_t rt_event_create (const char* name, rt_uint8_t flag) +{ + rt_event_t event; + + /* allocate object */ + event = (rt_event_t) rt_object_allocate(RT_Object_Class_Event, name); + if (event == RT_NULL) return event; + + /* set parent */ + event->parent.parent.flag = flag; + + /* init ipc object */ + rt_ipc_object_init(&(event->parent)); + + /* init event */ + event->set = 0; + + return event; +} + +/** + * This function will delete an event object and release the memory + * + * @param event the event object + * + * @return the error code + */ +rt_err_t rt_event_delete (rt_event_t event) +{ + /* parameter check */ + RT_ASSERT(event != RT_NULL); + + /* resume all suspended thread */ + rt_ipc_object_resume_all(&(event->parent)); + + /* delete event object */ + rt_object_delete(&(event->parent.parent)); + + return RT_EOK; +} +#endif + +/** + * This function will send an event to the event object, if there are threads + * suspended on event object, it will be waked up. + * + * @param event the event object + * @param set the event set + * + * @return the error code + */ +rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) +{ + struct rt_list_node *n; + struct rt_thread *thread; + register rt_ubase_t level; + register rt_base_t status; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + if (set == 0) return -RT_ERROR; + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(event->parent.parent)); +#endif + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* set event */ + event->set |= set; + + if (event->parent.suspend_thread_count > 0) + { + /* search thread list to resume thread */ + n = event->parent.suspend_thread.next; + while (n != &(event->parent.suspend_thread)) + { + /* get thread */ + thread = rt_list_entry(n, struct rt_thread, tlist); + + status = -RT_ERROR; + if (thread->event_info & RT_EVENT_FLAG_AND) + { + if ((thread->event_set & event->set) == thread->event_set) + { + status = RT_EOK; + } + } + else if (thread->event_info & RT_EVENT_FLAG_OR) + { + if (thread->event_set & event->set) + { + status = RT_EOK; + } + } + + /* move node to the nexe */ + n = n->next; + + /* condition is satisfied, resume thread */ + if (status == RT_EOK) + { + /* resume thread, and thread list breaks out */ + rt_thread_resume(thread); + + /* decrease suspended thread count */ + event->parent.suspend_thread_count--; + + if (thread->event_info & RT_EVENT_FLAG_CLEAR) + event->set &= ~thread->event_set; + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + return RT_EOK; +} + +/** + * This function will receive an event from event object, if the event is unavailable, + * the thread shall wait for a specified time. + * + * @param event the fast event object + * @param set the interested event set + * @param option the receive option + * @param timeout the waiting time + * @param recved the received event + * + * @return the error code + */ +rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t* recved) +{ + struct rt_thread *thread; + register rt_ubase_t level; + register rt_base_t status; + + /* parameter check */ + RT_ASSERT(event != RT_NULL); + if (set == 0) return -RT_ERROR; + + /* init status */ + status = -RT_ERROR; + +#ifdef RT_USING_HOOK + if (rt_object_trytake_hook != RT_NULL) rt_object_trytake_hook(&(event->parent.parent)); +#endif + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* check event set */ + if (option & RT_EVENT_FLAG_AND) + { + if ((event->set & set) == set) status = RT_EOK; + } + else if (option & RT_EVENT_FLAG_OR) + { + if (event->set & set) status = RT_EOK; + } + + /* get current thread */ + thread = rt_thread_self(); + /* reset thread error */ + thread->error = RT_EOK; + + if (status == RT_EOK) + { + /* set received event */ + *recved = event->set; + + /* received event */ + if (option & RT_EVENT_FLAG_CLEAR) event->set &= ~set; + } + else if (timeout == 0) + { + /* no waiting */ + thread->error = -RT_ETIMEOUT; + } + else + { + /* fill thread event info */ + thread->event_set = set; + thread->event_info = option; + + /* put thread to suspended thread list */ + rt_ipc_object_suspend(&(event->parent), thread); + + /* if there is a waiting timeout, active thread timer */ + if (timeout > 0) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) + { + /* decrease suspended thread count */ + rt_ipc_object_decrease(&(event->parent)); + return thread->error; + } + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* get received event */ + *recved = event->set; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(event->parent.parent)); +#endif + + return thread->error; +} + +/** + * This function can get or set some extra attributions of an event object. + * + * @param event the event object + * @param cmd the execution command + * @param arg the execution argument + * + * @return the error code + */ +rt_err_t rt_event_control (rt_event_t event, rt_uint8_t cmd, void* arg) +{ + return RT_EOK; +} + +#endif /* end of RT_USING_EVENT */ + +#ifdef RT_USING_MAILBOX + +/** + * This function will initialize a mailbox and put it under control of resource + * management. + * + * @param mb the mailbox object + * @param name the name of mailbox + * @param msgpool the begin address of buffer to save received mail + * @param size the size of mailbox + * @param flag the flag of mailbox + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_mb_init(rt_mailbox_t mb, const char* name, void* msgpool, rt_size_t size, rt_uint8_t flag) +{ + RT_ASSERT(mb != RT_NULL); + + /* init object */ + rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name); + + /* set parent flag */ + mb->parent.parent.flag = flag; + + /* init ipc object */ + rt_ipc_object_init(&(mb->parent)); + + /* init mailbox */ + mb->msg_pool = msgpool; + mb->size = size; + mb->entry = 0; + mb->in_offset = 0; + mb->out_offset = 0; + + return RT_EOK; +} + +/** + * This function will detach a mailbox from resource management + * + * @param mb the mailbox object + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_mb_detach(rt_mailbox_t mb) +{ + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + + /* resume all suspended thread */ + rt_ipc_object_resume_all(&(mb->parent)); + + /* detach mailbox object */ + rt_object_detach(&(mb->parent.parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a mailbox object from system resource + * + * @param name the name of mailbox + * @param size the size of mailbox + * @param flag the flag of mailbox + * + * @return the created mailbox, RT_NULL on error happen + */ +rt_mailbox_t rt_mb_create (const char* name, rt_size_t size, rt_uint8_t flag) +{ + rt_mailbox_t mb; + + /* allocate object */ + mb = (rt_mailbox_t) rt_object_allocate(RT_Object_Class_MailBox, name); + if (mb == RT_NULL) return mb; + + /* set parent */ + mb->parent.parent.flag = flag; + + /* init ipc object */ + rt_ipc_object_init(&(mb->parent)); + + /* init mailbox */ + mb->size = size; + mb->msg_pool = rt_malloc(mb->size * sizeof(rt_uint32_t)); + if (mb->msg_pool == RT_NULL) + { + /* delete mailbox object */ + rt_object_delete(&(mb->parent.parent)); + + return RT_NULL; + } + mb->entry = 0; + mb->in_offset = 0; + mb->out_offset = 0; + + return mb; +} + +/** + * This function will delete a mailbox object and release the memory + * + * @param mb the mailbox object + * + * @return the error code + */ +rt_err_t rt_mb_delete (rt_mailbox_t mb) +{ + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + + /* resume all suspended thread */ + rt_ipc_object_resume_all(&(mb->parent)); + + /* free mailbox pool */ + rt_free(mb->msg_pool); + + /* delete mailbox object */ + rt_object_delete(&(mb->parent.parent)); + + return RT_EOK; +} +#endif + +/** + * This function will send a mail to mailbox object, if there are threads suspended + * on mailbox object, it will be waked up. + * + * @param mb the mailbox object + * @param value the mail + * + * @return the error code + */ +rt_err_t rt_mb_send (rt_mailbox_t mb, rt_uint32_t value) +{ + register rt_ubase_t temp; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(mb->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* mailbox is full */ + if (mb->entry == mb->size) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + + /* set ptr */ + mb->msg_pool[mb->in_offset] = value; + /* increase input offset */ + ++ mb->in_offset; + mb->in_offset %= mb->size; + /* increase message entry */ + mb->entry ++; + + /* resume suspended thread */ + if (mb->parent.suspend_thread_count > 0) + { + rt_ipc_object_resume(&(mb->parent)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +/** + * This function will receive a mail from mailbox object, if there is no mail in + * mailbox object, the thread shall wait for a specified time. + * + * @param mb the mailbox object + * @param value the received mail will be saved in + * @param timeout the waiting time + * + * @return the error code + */ +rt_err_t rt_mb_recv (rt_mailbox_t mb, rt_uint32_t* value, rt_int32_t timeout) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + +#ifdef RT_USING_HOOK + if (rt_object_trytake_hook != RT_NULL) rt_object_trytake_hook(&(mb->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* mailbox is empty */ + if (mb->entry == 0) + { + /* get current thread */ + thread = rt_thread_self(); + + /* reset error number in thread */ + thread->error = RT_EOK; + + /* no waiting, return timeout */ + if (timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + thread->error = -RT_ETIMEOUT; + return -RT_ETIMEOUT; + } + + /* suspend current thread */ + rt_ipc_object_suspend(&(mb->parent), thread); + + /* has waiting time, start thread timer */ + if (timeout > 0) + { +#ifdef IPC_DEBUG + rt_kprintf("set thread:%s to timer list\n", thread->name); +#endif + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* re-schedule */ + rt_schedule(); + + /* recv message */ + if (thread->error != RT_EOK) + { + /* decrease suspended thread count */ + rt_ipc_object_decrease(&(mb->parent)); + return thread->error; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + } + + /* fill ptr */ + *value = mb->msg_pool[mb->out_offset]; + + /* increase output offset */ + ++mb->out_offset; + mb->out_offset %= mb->size; + /* decrease message entry */ + mb->entry --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(mb->parent.parent)); +#endif + + return RT_EOK; +} + +/** + * This function can get or set some extra attributions of a mailbox object. + * + * @param mb the mailbox object + * @param cmd the execution command + * @param arg the execution argument + * + * @return the error code + */ +rt_err_t rt_mb_control(rt_mailbox_t mb, rt_uint8_t cmd, void* arg) +{ + return RT_EOK; +} + +#endif /* end of RT_USING_MAILBOX */ + +#ifdef RT_USING_MESSAGEQUEUE + +struct rt_mq_message +{ + struct rt_mq_message* next; +}; + +/** + * This function will initialize a message queue and put it under control of resource + * management. + * + * @param mq the message object + * @param name the name of message queue + * @param msgpool the beginning address of buffer to save messages + * @param msg_size the maximum size of message + * @param pool_size the size of buffer to save messages + * @param flag the flag of message queue + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_mq_init(rt_mq_t mq, const char* name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag) +{ + struct rt_mq_message* head; + register rt_base_t temp; + + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + + /* init object */ + rt_object_init(&(mq->parent.parent), RT_Object_Class_MessageQueue, name); + + /* set parent flag */ + mq->parent.parent.flag = flag; + + /* init ipc object */ + rt_ipc_object_init(&(mq->parent)); + + /* set messasge pool */ + mq->msg_pool = msgpool; + + /* get correct message size */ + mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE); + mq->max_msgs = pool_size / (mq->msg_size + sizeof(struct rt_mq_message)); + + /* init message list */ + mq->msg_queue_head = RT_NULL; + mq->msg_queue_tail = RT_NULL; + + /* init message empty list */ + mq->msg_queue_free = RT_NULL; + for (temp = 0; temp < mq->max_msgs; temp ++) + { + head = (struct rt_mq_message*)((rt_uint8_t*)mq->msg_pool + + temp * (mq->msg_size + sizeof(struct rt_mq_message))); + head->next = mq->msg_queue_free; + mq->msg_queue_free = head; + } + + /* the initial entry is zero */ + mq->entry = 0; + + return RT_EOK; +} + +/** + * This function will detach a message queue object from resource management + * + * @param mq the message queue object + * + * @return the operation status, RT_EOK on successful + */ +rt_err_t rt_mq_detach(rt_mq_t mq) +{ + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + + /* resume all suspended thread */ + rt_ipc_object_resume_all((struct rt_ipc_object*)mq); + + /* detach message queue object */ + rt_object_detach(&(mq->parent.parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a message queue object from system resource + * + * @param name the name of message queue + * @param msg_size the size of message + * @param max_msgs the maximum number of message in queue + * @param flag the flag of message queue + * + * @return the created message queue, RT_NULL on error happen + */ +rt_mq_t rt_mq_create (const char* name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag) +{ + struct rt_messagequeue* mq; + struct rt_mq_message* head; + register rt_base_t temp; + + /* allocate object */ + mq = (rt_mq_t) rt_object_allocate(RT_Object_Class_MessageQueue, name); + if (mq == RT_NULL) return mq; + + /* set parent */ + mq->parent.parent.flag = flag; + + /* init ipc object */ + rt_ipc_object_init(&(mq->parent)); + + /* init message queue */ + + /* get correct message size */ + mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE); + mq->max_msgs = max_msgs; + + /* allocate message pool */ + mq->msg_pool = rt_malloc((mq->msg_size + sizeof(struct rt_mq_message))* mq->max_msgs); + if (mq->msg_pool == RT_NULL) + { + rt_mq_delete(mq); + return RT_NULL; + } + + /* init message list */ + mq->msg_queue_head = RT_NULL; + mq->msg_queue_tail = RT_NULL; + + /* init message empty list */ + mq->msg_queue_free = RT_NULL; + for (temp = 0; temp < mq->max_msgs; temp ++) + { + head = (struct rt_mq_message*)((rt_uint8_t*)mq->msg_pool + + temp * (mq->msg_size + sizeof(struct rt_mq_message))); + head->next = mq->msg_queue_free; + mq->msg_queue_free = head; + } + + /* the initial entry is zero */ + mq->entry = 0; + + return mq; +} + +/** + * This function will delete a message queue object and release the memory + * + * @param mq the message queue object + * + * @return the error code + */ +rt_err_t rt_mq_delete (rt_mq_t mq) +{ + /* parameter check */ + RT_ASSERT(mq != RT_NULL); + + /* resume all suspended thread */ + rt_ipc_object_resume_all(&(mq->parent)); + + /* free mailbox pool */ + rt_free(mq->msg_pool); + + /* delete mailbox object */ + rt_object_delete(&(mq->parent.parent)); + + return RT_EOK; +} +#endif + +/** + * This function will send a message to message queue object, if there are threads + * suspended on message queue object, it will be waked up. + * + * @param mq the message queue object + * @param buffer the message + * @param size the size of buffer + * + * @return the error code + */ +rt_err_t rt_mq_send (rt_mq_t mq, void* buffer, rt_size_t size) +{ + register rt_ubase_t temp; + struct rt_mq_message *msg; + + /* greater than one message size */ + if (size > mq->msg_size) return -RT_ERROR; + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(mq->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get a free list, there must be an empty item */ + msg = (struct rt_mq_message*)mq->msg_queue_free; + + /* message queue is full */ + if (msg == RT_NULL) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + + /* move free list pointer */ + mq->msg_queue_free = msg->next; + + /* copy buffer */ + rt_memcpy(msg + 1, buffer, size); + + /* link msg to message queue */ + if (mq->msg_queue_tail != RT_NULL) + { + /* if the tail exists, */ + ((struct rt_mq_message*)mq->msg_queue_tail)->next = msg; + } + /* the msg is the new tail of list, the next shall be NULL */ + msg->next = RT_NULL; + + /* set new tail */ + mq->msg_queue_tail = msg; + + /* if the head is empty, set head */ + if (mq->msg_queue_head == RT_NULL)mq->msg_queue_head = msg; + + /* increase message entry */ + mq->entry ++; + + /* resume suspended thread */ + if (mq->parent.suspend_thread_count > 0) + { + rt_ipc_object_resume(&(mq->parent)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +/** + * This function will send urgently a message to message queue object, which means + * the message will be inserted to the head of message queue. If there are threads + * suspended on message queue object, it will be waked up. + * + * @param mq the message queue object + * @param buffer the message + * @param size the size of buffer + * + * @return the error code + */ +rt_err_t rt_mq_urgent(rt_mq_t mq, void* buffer, rt_size_t size) +{ + register rt_ubase_t temp; + struct rt_mq_message *msg; + + /* greater than one message size */ + if (size > mq->msg_size) return -RT_ERROR; + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(mq->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get a free list, there must be an empty item */ + msg = (struct rt_mq_message*)mq->msg_queue_free; + + /* message queue is full */ + if (msg == RT_NULL) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return -RT_EFULL; + } + + /* move free list pointer */ + mq->msg_queue_free = msg->next; + + /* copy buffer */ + rt_memcpy(msg + 1, buffer, size); + + /* link msg to the beginning of message queue */ + msg->next = mq->msg_queue_head; + mq->msg_queue_head = msg; + + /* if there is no tail */ + if (mq->msg_queue_tail == RT_NULL) mq->msg_queue_tail = msg; + + /* increase message entry */ + mq->entry ++; + + /* resume suspended thread */ + if (mq->parent.suspend_thread_count > 0) + { + rt_ipc_object_resume(&(mq->parent)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +/** + * This function will receive a message from message queue object, if there is no + * message in message queue object, the thread shall wait for a specified time. + * + * @param mq the message queue object + * @param buffer the received message will be saved in + * @param size the size of buffer + * @param timeout the waiting time + * + * @return the error code + */ +rt_err_t rt_mq_recv (rt_mq_t mq, void* buffer, rt_size_t size, rt_int32_t timeout) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + struct rt_mq_message *msg; + +#ifdef RT_USING_HOOK + if (rt_object_trytake_hook != RT_NULL) rt_object_trytake_hook(&(mq->parent.parent)); +#endif + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* mailbox is empty */ + if (mq->entry == 0) + { + /* get current thread */ + thread = rt_thread_self(); + + /* reset error number in thread */ + thread->error = RT_EOK; + + /* no waiting, return timeout */ + if (timeout == 0) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + thread->error = -RT_ETIMEOUT; + return -RT_ETIMEOUT; + } + + /* suspend current thread */ + rt_ipc_object_suspend(&(mq->parent), thread); + + /* has waiting time, start thread timer */ + if (timeout > 0) + { +#ifdef IPC_DEBUG + rt_kprintf("set thread:%s to timer list\n", thread->name); +#endif + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* re-schedule */ + rt_schedule(); + + /* recv message */ + if (thread->error != RT_EOK) + { + /* decrease suspended thread count */ + rt_ipc_object_decrease(&(mq->parent)); + return thread->error; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + } + + /* get message from queue */ + msg = (struct rt_mq_message*) mq->msg_queue_head; + + /* move message queue head */ + mq->msg_queue_head = msg->next; + + /* reach queue tail, set to NULL */ + if (mq->msg_queue_tail == msg) mq->msg_queue_tail = RT_NULL; + + /* copy message */ + rt_memcpy(buffer, msg + 1, + size > mq->msg_size? mq->msg_size : size); + + /* put message to free list */ + msg->next = (struct rt_mq_message*)mq->msg_queue_free; + mq->msg_queue_free = msg; + + /* decrease message entry */ + mq->entry --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(mq->parent.parent)); +#endif + + return RT_EOK; +} + +/** + * This function can get or set some extra attributions of a message queue object. + * + * @param mq the message queue object + * @param cmd the execution command + * @param arg the execution argument + * + * @return the error code + */ +rt_err_t rt_mq_control(rt_mq_t mq, rt_uint8_t cmd, void* arg) +{ + return RT_EOK; +} + +#endif /* end of RT_USING_MESSAGEQUEUE */ + +/** + * @ingroup SystemInit + * This function will init IPC module. + */ +void rt_system_ipc_init() +{ + /* nothing to be done */ +} + +/*@}*/ diff --git a/src/irq.c b/src/irq.c new file mode 100644 index 0000000000..cdad403976 --- /dev/null +++ b/src/irq.c @@ -0,0 +1,69 @@ +/* + * File : irq.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-02-24 Bernard first version + * 2006-05-03 Bernard add IRQ_DEBUG + */ + +#include <rthw.h> +#include <rtthread.h> + +/* #define IRQ_DEBUG */ + +/** + * @addtogroup Kernel + */ + +/*@{*/ + +volatile rt_uint32_t rt_interrupt_nest; + +/** + * This function will be invoked by BSP, when enter interrupt service routine + * + * @note please don't invoke this routine in application + * + * @see rt_interrupt_leave + */ +void rt_interrupt_enter() +{ + rt_base_t level; + +#ifdef IRQ_DEBUG + rt_kprintf("irq comming..., irq nest:%d\n", rt_interrupt_nest); +#endif + + level = rt_hw_interrupt_disable(); + rt_interrupt_nest ++; + rt_hw_interrupt_enable(level); +} + +/** + * This function will be invoked by BSP, when leave interrupt service routine + * + * @note please don't invoke this routine in application + * + * @see rt_interrupt_enter + */ +void rt_interrupt_leave() +{ + rt_base_t level; + +#ifdef IRQ_DEBUG + rt_kprintf("irq leave, irq nest:%d\n", rt_interrupt_nest); +#endif + + level = rt_hw_interrupt_disable(); + rt_interrupt_nest --; + rt_hw_interrupt_enable(level); +} + +/*@}*/ diff --git a/src/kservice.c b/src/kservice.c new file mode 100644 index 0000000000..af2c4e5caa --- /dev/null +++ b/src/kservice.c @@ -0,0 +1,922 @@ +/* + * File : kservice.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-16 Bernard the first version + * 2006-05-25 Bernard rewrite vsprintf + * 2006-08-10 Bernard add rt_show_version + */ + +#include <rtthread.h> +#include <rthw.h> + +/** + * @addtogroup KernelService + */ +/*@{*/ + +#ifndef RT_USING_NEWLIB +/* global errno in RT-Thread*/ +int errno; +#else +#include <errno.h> +#endif + +/* + * This function will get errno + * + * @return errno + */ +rt_err_t rt_get_errno(void) +{ + rt_thread_t tid; + + tid = rt_thread_self(); + if (tid == RT_NULL) return errno; + + return tid->error; +} + +/* + * This function will set errno + * + * @param error the errno shall be set + */ +void rt_set_errno(rt_err_t error) +{ + rt_thread_t tid; + + tid = rt_thread_self(); + if (tid == RT_NULL) { errno = error; return; } + + tid->error = error; +} + +/** + * This function will set the content of memory to specified value + * + * @param s the address of source memory + * @param c the value shall be set in content + * @param count the copied length + * + * @return the address of source memory + * + */ +void *rt_memset(void * s, int c, rt_ubase_t count) +{ +#ifdef RT_TINY_SIZE + char *xs = (char *) s; + + while (count--) + *xs++ = c; + + return s; +#else +#define LBLOCKSIZE (sizeof(rt_int32_t)) +#define UNALIGNED(X) ((rt_int32_t)X & (LBLOCKSIZE - 1)) +#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE) + + int i; + char *m = (char *)s; + rt_uint32_t buffer; + rt_uint32_t *aligned_addr; + rt_uint32_t d = c & 0xff; + + if (!TOO_SMALL (count) && !UNALIGNED (s)) + { + /* If we get this far, we know that n is large and m is word-aligned. */ + aligned_addr = (rt_uint32_t*)s; + + /* Store D into each char sized location in BUFFER so that + * we can set large blocks quickly. + */ + if (LBLOCKSIZE == 4) + { + buffer = (d << 8) | d; + buffer |= (buffer << 16); + } + else + { + buffer = 0; + for (i = 0; i < LBLOCKSIZE; i++) + buffer = (buffer << 8) | d; + } + + while (count >= LBLOCKSIZE*4) + { + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + *aligned_addr++ = buffer; + count -= 4*LBLOCKSIZE; + } + + while (count >= LBLOCKSIZE) + { + *aligned_addr++ = buffer; + count -= LBLOCKSIZE; + } + + /* Pick up the remainder with a bytewise loop. */ + m = (char*)aligned_addr; + } + + while (count--) + { + *m++ = (char)d; + } + + return s; + +#undef LBLOCKSIZE +#undef UNALIGNED +#undef TOO_SMALL +#endif +} + +/** + * This function will copy memory content from source address to destination + * address. + * + * @param dst the address of destination memory + * @param src the address of source memory + * @param count the copied length + * + * @return the address of destination memory + * + */ +void *rt_memcpy(void * dst, const void *src, rt_ubase_t count) +{ +#ifdef RT_TINY_SIZE + char *tmp = (char *) dst, *s = (char *) src; + + while (count--) + *tmp++ = *s++; + + return dst; +#else + +#define UNALIGNED(X, Y) \ + (((rt_int32_t)X & (sizeof (rt_int32_t) - 1)) | ((rt_int32_t)Y & (sizeof (rt_int32_t) - 1))) +#define BIGBLOCKSIZE (sizeof (rt_int32_t) << 2) +#define LITTLEBLOCKSIZE (sizeof (rt_int32_t)) +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + + char *dst_ptr = (char*)dst; + char *src_ptr = (char*)src; + rt_int32_t *aligned_dst; + rt_int32_t *aligned_src; + int len = count; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len) && !UNALIGNED (src_ptr, dst_ptr)) + { + aligned_dst = (rt_int32_t*)dst_ptr; + aligned_src = (rt_int32_t*)src_ptr; + + /* Copy 4X long words at a time if possible. */ + while (len >= BIGBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len >= LITTLEBLOCKSIZE) + { + *aligned_dst++ = *aligned_src++; + len -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst_ptr = (char*)aligned_dst; + src_ptr = (char*)aligned_src; + } + + while (len--) + *dst_ptr++ = *src_ptr++; + + return dst; +#undef UNALIGNED +#undef BIGBLOCKSIZE +#undef LITTLEBLOCKSIZE +#undef TOO_SMALL +#endif +} + +/** + * This function will move memory content from source address to destination + * address. + * + * @param dest the address of destination memory + * @param src the address of source memory + * @param n the copied length + * + * @return the address of destination memory + * + */ +void* rt_memmove(void *dest, const void *src, rt_ubase_t n) +{ + char *tmp = (char *) dest, *s = (char *) src; + + if (s < tmp && tmp < s + n) + { + tmp+=n; + s+=n; + + while (n--) + *tmp-- = *s--; + } + else + { + while (n--) + *tmp++ = *s++; + } + + return dest; +} + +/** + * memcmp - Compare two areas of memory + * @param cs: One area of memory + * @param ct: Another area of memory + * @param count: The size of the area. + */ +rt_int32_t rt_memcmp(const void * cs,const void * ct, rt_ubase_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} + +/** + * This function will return the first occurrence of a string. + * + * @param s1 the source string + * @param s2 the find string + * + * @return the first occurrence of a s2 in s1, or RT_NULL if no found. + */ +char * rt_strstr(const char * s1,const char * s2) +{ + int l1, l2; + + l2 = rt_strlen(s2); + if (!l2) + return (char *) s1; + l1 = rt_strlen(s1); + while (l1 >= l2) + { + l1--; + if (!rt_memcmp(s1,s2,l2)) + return (char *) s1; + s1++; + } + return RT_NULL; +} + +/** + * This function will compare two strings while ignoring differences in case + * + * @param a the string to be compared + * @param b the string to be compared + * + * @return the result + */ +rt_uint32_t rt_strcasecmp(const char *a, const char *b) +{ + int ca, cb; + + do + { + ca = *a++ & 0xff; + cb = *b++ & 0xff; + if (ca >= 'A' && ca <= 'Z') + ca += 'a' - 'A'; + if (cb >= 'A' && cb <= 'Z') + cb += 'a' - 'A'; + } + while (ca == cb && ca != '\0'); + + return ca - cb; +} + +/** + * This function will copy string no more than n bytes. + * + * @param dest the string to copy + * @param src the string to be copied + * @param n the maximum copied length + * + * @return the result + */ +char *rt_strncpy(char *dest, const char *src, rt_ubase_t n) +{ + char *tmp = (char *) dest, *s = (char *) src; + + while(n--) + *tmp++ = *s++; + + return dest; +} + +/** + * This function will compare two strings with specified maximum length + * + * @param cs the string to be compared + * @param ct the string to be compared + * @param count the maximum compare length + * + * @return the result + */ +rt_ubase_t rt_strncmp(const char * cs, const char * ct, rt_ubase_t count) +{ + register signed char __res = 0; + + while (count) + { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + + return __res; +} + +/** + * This function will return the length of a string, which terminate will + * null character. + * + * @param s the string + * + * @return the length of string + */ +rt_ubase_t rt_strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) /* nothing */ + ; + + return sc - s; +} + +#ifdef RT_USING_HEAP +/** + * This function will duplicate a string. + * + * @param s the string to be duplicated + * + * @return the duplicated string pointer + */ +char *rt_strdup(const char *s) +{ + rt_size_t len = rt_strlen(s) + 1; + char *tmp = (char *)rt_malloc(len); + + if(!tmp) return RT_NULL; + + rt_memcpy(tmp, s, len); + return tmp; +} +#endif + +/** + * This function will show the version of rt-thread rtos + */ +void rt_show_version() +{ + rt_kprintf("\n \\ | /\n"); + rt_kprintf("- RT - Thread Operating System\n"); + rt_kprintf(" / | \\ 0.%d.%d build %s\n", RT_VERSION, RT_SUBVERSION, __DATE__); + rt_kprintf(" 2006 - 2009 Copyright by rt-thread team\n"); +} + +static char rt_log_buf[RT_CONSOLEBUF_SIZE]; /* Message log buffer */ + +/* private function */ +#define isdigit(c) ((unsigned)((c) - '0') < 10) + +rt_inline rt_int32_t divide(rt_int32_t *n, rt_int32_t base) +{ + rt_int32_t res; + + /* optimized for processor which does not support divide instructions. */ + if (base == 10) + { + res = ((rt_uint32_t)*n) % 10U; + *n = ((rt_uint32_t)*n) / 10U; + } + else + { + res = ((rt_uint32_t)*n) % 16U; + *n = ((rt_uint32_t)*n) / 16U; + } + + return res; +} + +rt_inline int skip_atoi(const char **s) +{ + register int i=0; + while (isdigit(**s)) i = i*10 + *((*s)++) - '0'; + + return i; +} + +#define ZEROPAD (1 << 0) /* pad with zero */ +#define SIGN (1 << 1) /* unsigned/signed long */ +#define PLUS (1 << 2) /* show plus */ +#define SPACE (1 << 3) /* space if plus */ +#define LEFT (1 << 4) /* left justified */ +#define SPECIAL (1 << 5) /* 0x */ +#define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */ + +#ifdef RT_PRINTF_PRECISION +static char *print_number(char * buf, char * end, long num, int base, int s, int precision, int type) +#else +static char *print_number(char * buf, char * end, long num, int base, int s, int type) +#endif +{ + char c, sign; +#ifdef RT_PRINTF_LONGLONG + char tmp[32]; +#else + char tmp[16]; +#endif + const char *digits; + static const char small_digits[] = "0123456789abcdef"; + static const char large_digits[] = "0123456789ABCDEF"; + register int i; + register int size; + + size = s; + + digits = (type & LARGE) ? large_digits : small_digits; + if (type & LEFT) type &= ~ZEROPAD; + + c = (type & ZEROPAD) ? '0' : ' '; + + /* get sign */ + sign = 0; + if (type & SIGN) + { + if (num < 0) + { + sign = '-'; + num = -num; + } + else if (type & PLUS) sign = '+'; + else if (type & SPACE) sign = ' '; + } + +#ifdef RT_PRINTF_SPECIAL + if (type & SPECIAL) + { + if (base == 16) size -= 2; + else if (base == 8) size--; + } +#endif + + i = 0; + if (num == 0) tmp[i++]='0'; + else + { + while (num != 0) tmp[i++] = digits[divide(&num, base)]; + } + +#ifdef RT_PRINTF_PRECISION + if (i > precision) precision = i; + size -= precision; +#else + size -= i; +#endif + + if (!(type&(ZEROPAD | LEFT))) + { + while(size-->0) + { + if (buf <= end) *buf = ' '; + ++buf; + } + } + + if (sign) + { + if (buf <= end) + { + *buf = sign; + --size; + } + ++buf; + } + +#ifdef RT_PRINTF_SPECIAL + if (type & SPECIAL) + { + if (base==8) + { + if (buf <= end) *buf = '0'; + ++buf; + } + else if (base==16) + { + if (buf <= end) *buf = '0'; + ++buf; + if (buf <= end) + { + *buf = type & LARGE? 'X' : 'x'; + } + ++buf; + } + } +#endif + + /* no align to the left */ + if (!(type & LEFT)) + { + while (size-- > 0) + { + if (buf <= end) *buf = c; + ++buf; + } + } + +#ifdef RT_PRINTF_PRECISION + while (i < precision--) + { + if (buf <= end) *buf = '0'; + ++buf; + } +#endif + + /* put number in the temporary buffer */ + while (i-- > 0) + { + if (buf <= end) *buf = tmp[i]; + ++buf; + } + + while (size-- > 0) + { + if (buf <= end) *buf = ' '; + ++buf; + } + + return buf; +} + +static rt_int32_t vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args) +{ +#ifdef RT_PRINTF_LONGLONG + unsigned long long num; +#else + rt_uint32_t num; +#endif + int i, len; + char *str, *end, c; + const char *s; + + rt_uint8_t base; /* the base of number */ + rt_uint8_t flags; /* flags to print number */ + rt_uint8_t qualifier; /* 'h', 'l', or 'L' for integer fields */ + rt_int32_t field_width; /* width of output field */ + +#ifdef RT_PRINTF_PRECISION + int precision; /* min. # of digits for integers and max for a string */ +#endif + + str = buf; + end = buf + size - 1; + + /* Make sure end is always >= buf */ + if (end < buf) + { + end = ((char *)-1); + size = end - buf; + } + + for (; *fmt ; ++fmt) + { + if (*fmt != '%') + { + if (str <= end) *str = *fmt; + ++str; + continue; + } + + /* process flags */ + flags = 0; + + while(1) + { + /* skips the first '%' also */ + ++fmt; + if (*fmt == '-') flags |= LEFT; + else if (*fmt == '+') flags |= PLUS; + else if (*fmt == ' ') flags |= SPACE; + else if (*fmt == '#') flags |= SPECIAL; + else if (*fmt == '0') flags |= ZEROPAD; + else break; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) field_width = skip_atoi(&fmt); + else if (*fmt == '*') + { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) + { + field_width = -field_width; + flags |= LEFT; + } + } + +#ifdef RT_PRINTF_PRECISION + /* get the precision */ + precision = -1; + if (*fmt == '.') + { + ++fmt; + if (isdigit(*fmt)) precision = skip_atoi(&fmt); + else if (*fmt == '*') + { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) precision = 0; + } +#endif + /* get the conversion qualifier */ + qualifier = 0; + if (*fmt == 'h' || *fmt == 'l' +#ifdef RT_PRINTF_LONGLONG + || *fmt == 'L' +#endif + ) + { + qualifier = *fmt; + ++fmt; +#ifdef RT_PRINTF_LONGLONG + if (qualifier == 'l' && *fmt == 'l') + { + qualifier = 'L'; + ++fmt; + } +#endif + } + + /* the default base */ + base = 10; + + switch (*fmt) + { + case 'c': + if (!(flags & LEFT)) + { + while (--field_width > 0) + { + if (str <= end) *str = ' '; + ++str; + } + } + + /* get character */ + c = (rt_uint8_t) va_arg(args, int); + if (str <= end) *str = c; + ++str; + + /* put width */ + while (--field_width > 0) + { + if (str <= end) *str = ' '; + ++str; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) s = "(NULL)"; + + len = rt_strlen(s); +#ifdef RT_PRINTF_PRECISION + if (precision > 0 && len > precision) len = precision; +#endif + + if (!(flags & LEFT)) + { + while (len < field_width--) + { + if (str <= end) *str = ' '; + ++str; + } + } + + for (i = 0; i < len; ++i) + { + if (str <= end) *str = *s; + ++str; + ++s; + } + + while (len < field_width--) + { + if (str <= end) *str = ' '; + ++str; + } + continue; + + case 'p': + if (field_width == -1) + { + field_width = sizeof(void *) << 1; + flags |= ZEROPAD; + } +#ifdef RT_PRINTF_PRECISION + str = print_number(str, end, + (long) va_arg(args, void *), + 16, field_width, precision, flags); +#else + str = print_number(str, end, + (long) va_arg(args, void *), + 16, field_width, flags); +#endif + continue; + + case '%': + if (str <= end) *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str <= end) *str = '%'; + ++str; + + if (*fmt) + { + if (str <= end) *str = *fmt; + ++str; + } + else + { + --fmt; + } + continue; + } + +#ifdef RT_PRINTF_LONGLONG + if (qualifier == 'L') num = va_arg(args, long long); + else if (qualifier == 'l') +#else + if (qualifier == 'l') +#endif + { + num = va_arg(args, rt_uint32_t); + if (flags & SIGN) num = (rt_int32_t) num; + } + else if (qualifier == 'h') + { + num = (rt_uint16_t) va_arg(args, rt_int32_t); + if (flags & SIGN) num = (rt_int16_t) num; + } + else + { + num = va_arg(args, rt_uint32_t); + if (flags & SIGN) num = (rt_int32_t) num; + } +#ifdef RT_PRINTF_PRECISION + str = print_number(str, end, num, base, field_width, precision, flags); +#else + str = print_number(str, end, num, base, field_width, flags); +#endif + } + + if (str <= end) *str = '\0'; + else *end = '\0'; + + /* the trailing null byte doesn't count towards the total + * ++str; + */ + return str-buf; +} + +/** + * This function will fill a formatted string to buffer + * + * @param buf the buffer to save formatted string + * @param size the size of buffer + * @param fmt the format + */ +rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...) +{ + rt_int32_t n; + va_list args; + + va_start(args, fmt); + n = vsnprintf(buf, size, fmt, args); + va_end(args); + + return n; +} + +/** + * This function will fill a formatted string to buffer + * + * @param buf the buffer to save formatted string + * @param arg_ptr the arg_ptr + * @param format the format + */ +rt_int32_t rt_vsprintf(char *buf, const char *format, va_list arg_ptr) +{ + return vsnprintf(buf, (rt_size_t) -1, format, arg_ptr); +} + +/** + * This function will fill a formatted string to buffer + * + * @param buf the buffer to save formatted string + * @param format the format + */ +rt_int32_t rt_sprintf(char *buf ,const char *format,...) +{ + rt_int32_t n; + va_list arg_ptr; + + va_start(arg_ptr, format); + n = rt_vsprintf(buf ,format,arg_ptr); + va_end (arg_ptr); + + return n; +} + + +/** + * This function will print a formatted string on system console + * + * @param fmt the format + */ +void rt_kprintf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + vsnprintf(rt_log_buf, sizeof(rt_log_buf), fmt, args); + rt_hw_console_output(rt_log_buf); + + va_end(args); +} + +#if !defined (RT_USING_NEWLIB) && defined (RT_USING_MINILIBC) && defined (__GNUC__) +#include <sys/types.h> +void* memcpy(void *dest, const void *src, size_t n) __attribute__((weak, alias("rt_memcpy"))); +void* memset(void *s, int c, size_t n) __attribute__((weak, alias("rt_memset"))); +void* memmove(void *dest, const void *src, size_t n) __attribute__((weak, alias("rt_memmove"))); +int memcmp(const void *s1, const void *s2, size_t n) __attribute__((weak, alias("rt_memcmp"))); + +size_t strlen(const char *s) __attribute__((weak, alias("rt_strlen"))); +char * strstr(const char * s1,const char * s2) __attribute__((weak, alias("rt_strstr"))); +int strcasecmp(const char *a, const char *b) __attribute__((weak, alias("rt_strcasecmp"))); +char *strncpy(char *dest, const char *src, size_t n) __attribute__((weak, alias("rt_strncpy"))); +int strncmp(const char * cs, const char * ct, size_t count) __attribute__((weak, alias("rt_strncmp"))); +#ifdef RT_USING_HEAP +char* strdup(const char* s) __attribute__((weak, alias("rt_strdup"))); +#endif +#endif + +/*@}*/ diff --git a/src/kservice.h b/src/kservice.h new file mode 100644 index 0000000000..2b82cbe9b8 --- /dev/null +++ b/src/kservice.h @@ -0,0 +1,100 @@ +/* + * File : kservice.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-16 Bernard the first version + * 2006-09-07 Bernard move the kservice APIs to rtthread.h + * 2007-06-27 Bernard fix the rt_list_remove bug + */ + +#ifndef __RT_SERVICE_H__ +#define __RT_SERVICE_H__ + +#include <rtthread.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief initialize a list + * + * @param l list to be initialized + */ +rt_inline void rt_list_init(rt_list_t *l) +{ + l->next = l->prev = l; +} + +/** + * @brief insert a node after a list + * + * @param l list to insert it + * @param n new node to be inserted + */ +rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n) +{ + l->next->prev = n; + n->next = l->next; + + l->next = n; + n->prev = l; +} + +/** + * @brief insert a node before a list + * + * @param n new node to be inserted + * @param l list to insert it + */ +rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n) +{ + l->prev->next = n; + n->prev = l->prev; + + l->prev = n; + n->next = l; +} + +/** + * @brief remove node from list. + * @param n the node to remove from the list. + */ +rt_inline void rt_list_remove(rt_list_t *n) +{ + n->next->prev = n->prev; + n->prev->next = n->next; + + n->next = n->prev = n; +} + +/** + * @brief tests whether a list is empty + * @param l the list to test. + */ +rt_inline int rt_list_isempty(const rt_list_t *l) +{ + return l->next == l; +} + +/** + * @brief get the struct for this entry + * @param node the entry point + * @param type the type of structure + * @param member the name of list in structure + */ +#define rt_list_entry(node, type, member) \ + ((type *)((char *)(node) - (unsigned long)(&((type *)0)->member))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000000..ba080a5bcb --- /dev/null +++ b/src/mem.c @@ -0,0 +1,504 @@ +/* + * File : kservice.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2008, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2008-7-12 Bernard the first version + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <[email protected]> + * Simon Goldschmidt + * + */ + +#include <rtthread.h> + +/* #define RT_MEM_DEBUG */ +#define RT_MEM_STATS
+ +#if defined (RT_USING_HEAP) && defined (RT_USING_SMALL_MEM) +#ifdef RT_USING_HOOK +static void (*rt_malloc_hook)(void *ptr, rt_size_t size); +static void (*rt_free_hook)(void *ptr); + +/** + * @addtogroup Hook + */ +/*@{*/ + +/** + * This function will set a hook function, which will be invoked when a memory + * block is allocated from heap memory. + * + * @param hook the hook function + */ +void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) +{ + rt_malloc_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when a memory + * block is released to heap memory. + * + * @param hook the hook function + */ +void rt_free_sethook(void (*hook)(void *ptr)) +{ + rt_free_hook = hook; +} + +/*@}*/ + +#endif + +#define HEAP_MAGIC 0x1ea0 +struct heap_mem +{ + /* magic and used flag */ + rt_uint16_t magic; + rt_uint16_t used; + + rt_size_t next, prev; +}; + +/** pointer to the heap: for alignment, heap_ptr is now a pointer instead of an array */ +static rt_uint8_t *heap_ptr; + +/** the last entry, always unused! */ +static struct heap_mem *heap_end; + +#define MIN_SIZE 12 +#define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE) +#define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct heap_mem), RT_ALIGN_SIZE) + +static struct heap_mem *lfree; /* pointer to the lowest free block */ + +static struct rt_semaphore heap_sem; +static rt_size_t mem_size_aligned;
+
+#ifdef RT_MEM_STATS +static rt_size_t used_mem, max_mem;
+#endif
+ +static void plug_holes(struct heap_mem *mem) +{ + struct heap_mem *nmem; + struct heap_mem *pmem; + + RT_ASSERT((rt_uint8_t *)mem >= heap_ptr); + RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)heap_end); + RT_ASSERT(mem->used == 0); + + /* plug hole forward */ + nmem = (struct heap_mem *)&heap_ptr[mem->next]; + if (mem != nmem && nmem->used == 0 && (rt_uint8_t *)nmem != (rt_uint8_t *)heap_end) + { + /* if mem->next is unused and not end of heap_ptr, combine mem and mem->next */ + if (lfree == nmem) + { + lfree = mem; + } + mem->next = nmem->next; + ((struct heap_mem *)&heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - heap_ptr; + } + + /* plug hole backward */ + pmem = (struct heap_mem *)&heap_ptr[mem->prev]; + if (pmem != mem && pmem->used == 0) + { + /* if mem->prev is unused, combine mem and mem->prev */ + if (lfree == mem) + { + lfree = pmem; + } + pmem->next = mem->next; + ((struct heap_mem *)&heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - heap_ptr; + } +} + +/** + * @ingroup SystemInit + * + * This function will init system heap + * + * @param begin_addr the beginning address of system page + * @param end_addr the end address of system page + * + */ +void rt_system_heap_init(void* begin_addr, void* end_addr) +{ + struct heap_mem *mem; + + /* alignment addr */ + begin_addr = (void*)RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE); + + /* calculate the aligned memory size */ + mem_size_aligned = RT_ALIGN((rt_uint32_t)end_addr - (rt_uint32_t)begin_addr, RT_ALIGN_SIZE) - 2 * sizeof(struct heap_mem); + + /* point to begin address of heap */ + heap_ptr = begin_addr; + +#ifdef RT_MEM_DEBUG + rt_kprintf("mem init, heap begin address 0x%x, size %d\n", (rt_uint32_t)heap_ptr, mem_size_aligned); +#endif + + /* initialize the start of the heap */ + mem = (struct heap_mem *)heap_ptr; + mem->magic= HEAP_MAGIC; + mem->next = mem_size_aligned; + mem->prev = 0; + mem->used = 0; + + /* initialize the end of the heap */ + heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; + heap_end->magic= HEAP_MAGIC; + heap_end->used = 1; + heap_end->next = mem_size_aligned; + heap_end->prev = mem_size_aligned; + + rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct heap_mem *)heap_ptr;
+} + +/** + * @addtogroup MM + */ + +/*@{*/ + +/** + * Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * + * @return pointer to allocated memory or NULL if no free memory was found. + */ +void *rt_malloc(rt_size_t size) +{ + rt_size_t ptr, ptr2; + struct heap_mem *mem, *mem2; + + if (size == 0) return RT_NULL; + +#ifdef RT_MEM_DEBUG + rt_kprintf("malloc size %d, but align to %d\n", size, RT_ALIGN(size, RT_ALIGN_SIZE)); +#endif + + /* alignment size */ + size = RT_ALIGN(size, RT_ALIGN_SIZE); + + if (size > mem_size_aligned) + { +#ifdef RT_MEM_DEBUG + rt_kprintf("no memory\n"); +#endif + return RT_NULL; + } + + /* every data block must be at least MIN_SIZE_ALIGNED long */ + if(size < MIN_SIZE_ALIGNED) size = MIN_SIZE_ALIGNED; + + /* take memory semaphore */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + for (ptr = (rt_uint8_t *)lfree - heap_ptr; ptr < mem_size_aligned - size; + ptr = ((struct heap_mem *)&heap_ptr[ptr])->next) + { + mem = (struct heap_mem *)&heap_ptr[ptr]; + + if ((!mem->used) && + (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) + { + /* mem is not used and at least perfect fit is possible: + * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ + + if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) + { + /* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing + * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') + * -> split large block, create empty remainder, + * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if + * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, + * struct heap_mem would fit in but no data between mem2 and mem2->next + * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty + * region that couldn't hold data, but when mem->next gets freed, + * the 2 regions would be combined, resulting in more free memory + */ + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + + /* create mem2 struct */ + mem2 = (struct heap_mem *)&heap_ptr[ptr2]; + mem2->magic = HEAP_MAGIC; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + + /* and insert it between mem and mem->next */ + mem->next = ptr2; + mem->used = 1; + + if (mem2->next != mem_size_aligned) + { + ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; + }
+#ifdef RT_MEM_STATS
+ used_mem += (size + SIZEOF_STRUCT_MEM);
+ if (max_mem < used_mem) max_mem = used_mem;
+#endif + } + else + { + /* (a mem2 struct does no fit into the user data space of mem and mem->next will always + * be used at this point: if not we have 2 unused structs in a row, plug_holes should have + * take care of this). + * -> near fit or excact fit: do not split, no mem2 creation + * also can't move mem->next directly behind mem, since mem->next + * will always be used at this point! + */ + mem->used = 1;
+#ifdef RT_MEM_STATS
+ used_mem += mem->next - ((rt_uint8_t*)mem - heap_ptr);
+ if (max_mem < used_mem) max_mem = used_mem;
+#endif + } + + if (mem == lfree) + { + /* Find next free block after mem and update lowest free pointer */ + while (lfree->used && lfree != heap_end) lfree = (struct heap_mem *)&heap_ptr[lfree->next]; + + RT_ASSERT(((lfree == heap_end) || (!lfree->used))); + } + + rt_sem_release(&heap_sem); + RT_ASSERT((rt_uint32_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_uint32_t)heap_end); + RT_ASSERT((rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0); + RT_ASSERT((((rt_uint32_t)mem) & (RT_ALIGN_SIZE-1)) == 0); + +#ifdef RT_MEM_DEBUG + rt_kprintf("allocate memory at 0x%x\n", (rt_uint32_t)((rt_uint8_t*)mem + SIZEOF_STRUCT_MEM)); +#endif + +#ifdef RT_USING_HOOK + if (rt_malloc_hook != RT_NULL) + rt_malloc_hook((rt_uint8_t*)mem, size); +#endif + /* return the memory data except mem struct */ + return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; + } + } + + rt_sem_release(&heap_sem); + return RT_NULL; +} + +/** + * This function will change the previously allocated memory block. + * + * @param rmem pointer to memory allocated by rt_malloc + * @param newsize the required new size + * + * @return the changed memory block address + */ +void *rt_realloc(void *rmem, rt_size_t newsize) +{ + rt_size_t size; + rt_size_t ptr, ptr2; + struct heap_mem *mem, *mem2; + + /* alignment size */ + newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE); + if (newsize > mem_size_aligned) + { +#ifdef RT_MEM_DEBUG + rt_kprintf("no memory\n"); +#endif + return RT_NULL; + } + + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || + (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) + { + /* illegal memory */ + rt_sem_release(&heap_sem); + return rmem; + } + + mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); + + ptr = (rt_uint8_t *)mem - heap_ptr; + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + + if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) + {
+#if MEM_STATS
+ used_mem -= (size - newsize);
+#endif /* MEM_STATS */
+ + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct heap_mem *)&heap_ptr[ptr2]; + mem2->magic= HEAP_MAGIC; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != mem_size_aligned) + { + ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; + } + + plug_holes(mem2); + + rt_sem_release(&heap_sem); + return rmem; + } + rt_sem_release(&heap_sem); + + /* expand memory */ + mem2 = rt_malloc(newsize); + rt_memcpy(mem2, mem, size); + + rt_free(mem); + + return mem2; +} + +/** + * This function will contiguously allocate enough space for count objects + * that are size bytes of memory each and returns a pointer to the allocated + * memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *rt_calloc(rt_size_t count, rt_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = rt_malloc(count * size); + + /* zero the memory */ + if (p) rt_memset(p, 0, count * size); + + return p; +} + +/** + * This function will release the previously allocated memory block by rt_malloc. + * The released memory block is taken back to system heap. + * + * @param rmem the address of memory which will be released + */ +void rt_free(void *rmem) +{ + struct heap_mem *mem; + + if (rmem == RT_NULL) return; + RT_ASSERT((((rt_uint32_t)rmem) & (RT_ALIGN_SIZE-1)) == 0); + +#ifdef RT_USING_HOOK + if (rt_free_hook != RT_NULL) rt_free_hook(rmem); +#endif + +#ifdef RT_MEM_DEBUG + rt_kprintf("release memory 0x%x\n", (rt_uint32_t)rmem); +#endif + + /* protect the heap from concurrent access */ + rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + + RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)heap_ptr && + (rt_uint8_t *)rmem < (rt_uint8_t *)heap_end); + + if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) + { +#ifdef RT_MEM_DEBUG + rt_kprintf("illegal memory\n"); +#endif + /* illegal memory */ + rt_sem_release(&heap_sem); + return; + } + + /* Get the corresponding struct heap_mem ... */ + mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... which has to be in a used state ... */ + RT_ASSERT(mem->used); + /* ... and is now unused. */ + mem->used = 0; + + if (mem < lfree) + { + /* the newly freed struct is now the lowest */ + lfree = mem; + }
+ +#ifdef RT_MEM_STATS
+ used_mem -= (mem->next - ((rt_uint8_t*)mem - heap_ptr));
+#endif
+ + /* finally, see if prev or next are free also */ + plug_holes(mem); + rt_sem_release(&heap_sem); +}
+ +#ifdef RT_MEM_STATS +#ifdef RT_USING_FINSH +#include <finsh.h> +void list_mem() +{ + rt_kprintf("total memory: %d\n", mem_size_aligned); + rt_kprintf("used memory : %d\n", used_mem); + rt_kprintf("maximum allocated memory: %d\n", max_mem); +} +FINSH_FUNCTION_EXPORT(list_mem, list memory usage information) +#endif +#endif
+ +/*@}*/ + +#endif diff --git a/src/mempool.c b/src/mempool.c new file mode 100644 index 0000000000..3c5637d20e --- /dev/null +++ b/src/mempool.c @@ -0,0 +1,410 @@ +/* + * File : partition.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-05-27 Bernard implement memory pool + * 2006-06-03 Bernard fix the thread timer init bug + * 2006-06-30 Bernard fix the allocate/free block bug + * 2006-08-04 Bernard add hook support + * 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc + */ + +#include <rthw.h> +#include <rtthread.h> + +#include "kservice.h" + +#ifdef RT_USING_MEMPOOL + +#ifdef RT_USING_HOOK +static void (*rt_mp_alloc_hook)(void *block); +static void (*rt_mp_free_hook)(void *block); + +/** + * @addtogroup Hook + */ +/*@{*/ + +/** + * This function will set a hook function, which will be invoked when a memory + * block is allocated from memory pool. + * + * @param hook the hook function + */ +void rt_mp_alloc_sethook(void (*hook)(void *block)) +{ + rt_mp_alloc_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when a memory + * block is released to memory pool. + * + * @param hook the hook function + */ +void rt_mp_free_sethook(void (*hook)(void *block)) +{ + rt_mp_free_hook = hook; +} + +/*@}*/ +#endif + +/** + * @addtogroup MM + */ + +/*@{*/ + +/** + * This function will initialize a mempool object, normally which is used for static object. + * + * @param mp the mempool object + * @param name the name of memory pool + * @param start the star address of memory pool + * @param size the total size of memory pool + * @param block_size the size for each block + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_mp_init(struct rt_mempool* mp, const char* name, void *start, rt_size_t size, rt_size_t block_size) +{ + rt_uint8_t *block_ptr; + register rt_base_t offset; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + + /* init object */ + rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name); + + /* init memory pool */ + mp->start_address = start; + mp->size = RT_ALIGN(size, RT_ALIGN_SIZE); + + mp->block_size = block_size; + + /* align to align size byte */ + mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t*)); + mp->block_free_count = mp->block_total_count; + + /* init suspended thread list */ + rt_list_init(&(mp->suspend_thread)); + mp->suspend_thread_count = 0; + + /* init free block list */ + block_ptr = (rt_uint8_t*) mp->start_address; + for (offset = 0; offset < mp->block_total_count; offset ++) + { + *(rt_uint8_t**)(block_ptr + offset * (block_size + sizeof(rt_uint8_t*))) + = (rt_uint8_t*)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t*))); + } + + *(rt_uint8_t**)(block_ptr + offset * (block_size + sizeof(rt_uint8_t*))) = RT_NULL; + + mp->block_list = block_ptr; + + return RT_EOK; +} + +rt_err_t rt_mp_detach(struct rt_mempool* mp) +{ + struct rt_thread* thread; + register rt_ubase_t temp; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + + /* wakeup all suspended threads */ + while (!rt_list_isempty(&(mp->suspend_thread))) + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* + * resume thread + * In rt_thread_resume function, it will remove current thread from suspend + * list + */ + rt_thread_resume(thread); + + /* decrease suspended thread count */ + mp->suspend_thread_count --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } + + /* detach object */ + rt_object_detach(&(mp->parent)); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a mempool object and allocate the memory pool from heap. + * + * @param name the name of memory pool + * @param block_count the count of blocks in memory pool + * @param block_size the size for each block + * + * @return the created mempool object + * + */ +rt_mp_t rt_mp_create(const char* name, rt_size_t block_count, rt_size_t block_size) +{ + rt_uint8_t *block_ptr; + struct rt_mempool* mp; + register rt_base_t offset; + + /* allocate object */ + mp = (struct rt_mempool*)rt_object_allocate(RT_Object_Class_MemPool, name); + + /* init memory pool */ + mp->block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE); + mp->size = (block_size + sizeof(rt_uint8_t*))* block_count; + + /* allocate memory */ + mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t*))* block_count); + if (mp->start_address == RT_NULL) + { + /* no memory, delete memory pool object */ + rt_object_delete(&(mp->parent)); + + return RT_NULL; + } + + mp->block_total_count = block_count; + mp->block_free_count = mp->block_total_count; + + /* init suspended thread list */ + rt_list_init(&(mp->suspend_thread)); + mp->suspend_thread_count = 0; + + /* init free block list */ + block_ptr = (rt_uint8_t*) mp->start_address; + for (offset = 0; offset < mp->block_total_count; offset ++) + { + *(rt_uint8_t**)(block_ptr + offset * (block_size + sizeof(rt_uint8_t*))) + = block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t*)); + } + + *(rt_uint8_t**)(block_ptr + offset * (block_size + sizeof(rt_uint8_t*))) = RT_NULL; + + mp->block_list = block_ptr; + + return mp; +} + +/** + * This function will delete a memory pool and release the object memory. + * + * @param mp the memory pool object + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_mp_delete(rt_mp_t mp) +{ + struct rt_thread* thread; + register rt_ubase_t temp; + + /* parameter check */ + RT_ASSERT(mp != RT_NULL); + + /* wakeup all suspended threads */ + while (!rt_list_isempty(&(mp->suspend_thread))) + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get next suspend thread */ + thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); + /* set error code to RT_ERROR */ + thread->error = -RT_ERROR; + + /* + * resume thread + * In rt_thread_resume function, it will remove current thread from suspend + * list + */ + rt_thread_resume(thread); + + /* decrease suspended thread count */ + mp->suspend_thread_count --; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } + + /* release allocated room */ + rt_free(mp->start_address); + + /* detach object */ + rt_object_delete(&(mp->parent)); + + return RT_EOK; +} +#endif + +/** + * This function will allocate a block from memory pool + * + * @param mp the memory pool object + * @param time the waiting time + * + * @return the allocated memory block + * + */ +void *rt_mp_alloc (rt_mp_t mp, rt_int32_t time) +{ + rt_uint8_t* block_ptr; + register rt_base_t level; + struct rt_thread* thread; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + if(mp->block_free_count) + { + /* memory block is available. decrease the free block counter */ + mp->block_free_count--; + + /* get block from block list */ + block_ptr = mp->block_list; + mp->block_list = *(rt_uint8_t**)block_ptr; + + /* point to memory pool */ + *(rt_uint8_t**)block_ptr = (rt_uint8_t*)mp; + } + else + { + /* memory block is unavailable. */ + if (time == 0) return RT_NULL; + else + { + /* get current thread */ + thread = rt_thread_self(); + + /* need suspend thread */ + rt_thread_suspend(thread); + rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); + mp->suspend_thread_count++; + + if (time > 0) + { + /* init thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &time); + rt_timer_start(&(thread->thread_timer)); + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + if (thread->error != RT_EOK) return RT_NULL; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* decrease free block */ + mp->block_free_count --; + + /* get block from block list */ + block_ptr = mp->block_list; + mp->block_list = *(rt_uint8_t**)block_ptr; + + /* point to memory pool */ + *(rt_uint8_t**)block_ptr = (rt_uint8_t*)mp; + } + } + + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef RT_USING_HOOK + if (rt_mp_alloc_hook != RT_NULL) rt_mp_alloc_hook((rt_uint8_t*)(block_ptr + sizeof(rt_uint8_t*))); +#endif + + return (rt_uint8_t*)(block_ptr + sizeof(rt_uint8_t*)); +} + +/** + * This function will release a memory block + * + * @param block the address of memory block to be released + * + */ +void rt_mp_free (void *block) +{ + rt_uint8_t **block_ptr; + struct rt_mempool *mp; + struct rt_thread *thread; + register rt_base_t level; + + /* get the control block of pool which the block belongs to */ + block_ptr = (rt_uint8_t**)((rt_uint8_t*)block - sizeof(rt_uint8_t*)); + mp = (struct rt_mempool*) *block_ptr; + +#ifdef RT_USING_HOOK + if (rt_mp_free_hook != RT_NULL) rt_mp_free_hook(block); +#endif + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* increase the free block count */ + mp->block_free_count ++; + + /* link the block into the block list */ + *block_ptr = mp->block_list; + mp->block_list = (rt_uint8_t*)block_ptr; + + if (mp->suspend_thread_count > 0) + { + /* get the suspended thread */ + thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); + + /* set error */ + thread->error = RT_EOK; + + /* resume thread */ + rt_thread_resume(thread); + + /* decrease suspended thread count */ + mp->suspend_thread_count --; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* do a schedule */ + rt_schedule(); + + return; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} + +#endif + +/*@}*/ + diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000000..d3c6828bd8 --- /dev/null +++ b/src/object.c @@ -0,0 +1,401 @@ +/* + * File : object.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-14 Bernard the first version + * 2006-04-21 Bernard change the scheduler lock to interrupt lock + * 2006-05-18 Bernard fix the object init bug + * 2006-08-03 Bernard add hook support + * 2007-01-28 Bernard rename RT_OBJECT_Class_Static to RT_Object_Class_Static + */ + +#include <rtthread.h> +#include <rthw.h> + +#include "kservice.h" + +struct rt_object_information rt_object_container[RT_Object_Class_Unknown]; + +#ifdef RT_USING_HOOK +static void (*rt_object_attach_hook)(struct rt_object* object); +static void (*rt_object_detach_hook)(struct rt_object* object); +void (*rt_object_trytake_hook)(struct rt_object* object); +void (*rt_object_take_hook)(struct rt_object* object); +void (*rt_object_put_hook)(struct rt_object* object); + +/** + * @addtogroup Hook + */ +/*@{*/ + +/** + * This function will set a hook function, which will be invoked when object + * attaches to kernel object system. + * + * @param hook the hook function + */ +void rt_object_attach_sethook(void (*hook)(struct rt_object* object)) +{ + rt_object_attach_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * detaches from kernel object system. + * + * @param hook the hook function + */ +void rt_object_detach_sethook(void (*hook)(struct rt_object* object)) +{ + rt_object_detach_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * is taken from kernel object system. + * + * The object is taken means that + * semaphore - semaphore is taken by thread + * mutex - mutex is taken by thread + * event/fast event - event/fast event is received by thread + * mailbox - mail is received by thread + * message queue - message is received by thread + * + * @param hook the hook function + */ +void rt_object_trytake_sethook(void (*hook)(struct rt_object* object)) +{ + rt_object_trytake_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * have been taken from kernel object system. + * + * The object have been taken means that + * semaphore - semaphore have been taken by thread + * mutex - mutex have been taken by thread + * event/fast event - event/fast event have been received by thread + * mailbox - mail have been received by thread + * message queue - message have been received by thread + * timer - timer is started + * + * @param hook the hook function + */ +void rt_object_take_sethook(void (*hook)(struct rt_object* object)) +{ + rt_object_take_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when object + * is put to kernel object system. + * + * @param hook the hook function + */ +void rt_object_put_sethook(void (*hook)(struct rt_object* object)) +{ + rt_object_put_hook = hook; +} + +/*@}*/ +#endif + +/** + * @ingroup SystemInit + * + * This function will initialize system object management + * + */ +void rt_system_object_init(void) +{ + /* init object container - thread */ + rt_list_init(&(rt_object_container[RT_Object_Class_Thread].object_list)); + rt_object_container[RT_Object_Class_Thread].object_size = sizeof(struct rt_thread); + rt_object_container[RT_Object_Class_Thread].type = RT_Object_Class_Thread; + +#ifdef RT_USING_MODULE
+ /* init object container - module */ + rt_list_init(&(rt_object_container[RT_Object_Class_Module].object_list)); + rt_object_container[RT_Object_Class_Module].object_size = sizeof(struct rt_module); + rt_object_container[RT_Object_Class_Module].type = RT_Object_Class_Module; +#endif + +#ifdef RT_USING_SEMAPHORE + /* init object container - semaphore */ + rt_list_init(&(rt_object_container[RT_Object_Class_Semaphore].object_list)); + rt_object_container[RT_Object_Class_Semaphore].object_size = sizeof(struct rt_semaphore); + rt_object_container[RT_Object_Class_Semaphore].type = RT_Object_Class_Semaphore; +#endif + +#ifdef RT_USING_MUTEX + /* init object container - mutex */ + rt_list_init(&(rt_object_container[RT_Object_Class_Mutex].object_list)); + rt_object_container[RT_Object_Class_Mutex].object_size = sizeof(struct rt_mutex); + rt_object_container[RT_Object_Class_Mutex].type = RT_Object_Class_Mutex; +#endif + +#ifdef RT_USING_FASTEVENT + /* init object container - fast event */ + rt_list_init(&(rt_object_container[RT_Object_Class_FastEvent].object_list)); + rt_object_container[RT_Object_Class_FastEvent].object_size = sizeof(struct rt_fast_event); + rt_object_container[RT_Object_Class_FastEvent].type = RT_Object_Class_FastEvent; +#endif + +#ifdef RT_USING_EVENT + /* init object container - event */ + rt_list_init(&(rt_object_container[RT_Object_Class_Event].object_list)); + rt_object_container[RT_Object_Class_Event].object_size = sizeof(struct rt_event); + rt_object_container[RT_Object_Class_Event].type = RT_Object_Class_Event; +#endif + +#ifdef RT_USING_MAILBOX + /* init object container - mailbox */ + rt_list_init(&(rt_object_container[RT_Object_Class_MailBox].object_list)); + rt_object_container[RT_Object_Class_MailBox].object_size = sizeof(struct rt_mailbox); + rt_object_container[RT_Object_Class_MailBox].type = RT_Object_Class_MailBox; +#endif + +#ifdef RT_USING_MESSAGEQUEUE + /* init object container - message queue */ + rt_list_init(&(rt_object_container[RT_Object_Class_MessageQueue].object_list)); + rt_object_container[RT_Object_Class_MessageQueue].object_size = sizeof(struct rt_messagequeue); + rt_object_container[RT_Object_Class_MessageQueue].type = RT_Object_Class_MessageQueue; +#endif + +#ifdef RT_USING_MEMPOOL + /* init object container - memory pool */ + rt_list_init(&(rt_object_container[RT_Object_Class_MemPool].object_list)); + rt_object_container[RT_Object_Class_MemPool].object_size = sizeof(struct rt_mempool); + rt_object_container[RT_Object_Class_MemPool].type = RT_Object_Class_MemPool; +#endif + +#ifdef RT_USING_DEVICE + /* init object container - device */ + rt_list_init(&(rt_object_container[RT_Object_Class_Device].object_list)); + rt_object_container[RT_Object_Class_Device].object_size = sizeof(struct rt_device); + rt_object_container[RT_Object_Class_Device].type = RT_Object_Class_Device; +#endif + + /* init object container - timer */ + rt_list_init(&(rt_object_container[RT_Object_Class_Timer].object_list)); + rt_object_container[RT_Object_Class_Timer].object_size = sizeof(struct rt_timer); + rt_object_container[RT_Object_Class_Timer].type = RT_Object_Class_Timer; +} + +/** + * @addtogroup KernelObject + */ +/*@{*/ + +/** + * This function will init an object and add it to object system management. + * + * @param object the specified object to be initialized. + * @param type the object type. + * @param name the object name. In system, the object's name must + * be unique. + */ +void rt_object_init(struct rt_object* object, enum rt_object_class_type type, const char* name) +{ + register rt_base_t temp; + struct rt_object_information* information; + + /* get object information */ + information = &rt_object_container[type]; + + /* init object's parameters */ + + /* set object type to static */ + object->type = type | RT_Object_Class_Static; + + /* copy name */ + for (temp = 0; temp < RT_NAME_MAX; temp ++) + { + object->name[temp] = name[temp]; + } + +#ifdef RT_USING_HOOK + if (rt_object_attach_hook != RT_NULL) + { + rt_object_attach_hook(object); + } +#endif + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + + /* insert object into information object list */ + rt_list_insert_after(&(information->object_list), &(object->list)); + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); +} + +/** + * This function will detach a static object from object system, + * and the memory of static object is not freed. + * + * @param object the specified object to be detached. + */ +void rt_object_detach(rt_object_t object) +{ + register rt_base_t temp; + + /* object check */ + RT_ASSERT(object != RT_NULL); + +#ifdef RT_USING_HOOK + if (rt_object_detach_hook != RT_NULL) rt_object_detach_hook(object); +#endif + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + + /* remove from old list */ + rt_list_remove(&(object->list)); + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); +} + +#ifdef RT_USING_HEAP +/** + * This function will allocate an object from object system + * + * @param type the type of object + * @param name the object name. In system, the object's name must be unique. + * + * @return object + */ +rt_object_t rt_object_allocate(enum rt_object_class_type type, const char* name) +{ + struct rt_object* object; + register rt_base_t temp; + struct rt_object_information* information; + + /* get object information */ + information = &rt_object_container[type]; + + object = (struct rt_object*)rt_malloc(information->object_size); + if (object == RT_NULL) + { + /* no memory can be allocated */ + return RT_NULL; + } + + /* init object's parameters */ + + /* set object type */ + object->type = type; + + /* copy name */ + for (temp = 0; temp < RT_NAME_MAX; temp ++) + { + object->name[temp] = name[temp]; + } + +#ifdef RT_USING_HOOK + if (rt_object_attach_hook != RT_NULL) rt_object_attach_hook(object); +#endif + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + + /* insert object into information object list */ + rt_list_insert_after(&(information->object_list), &(object->list)); + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); + + /* return object */ + return object; +} + +/** + * This function will delete an object and release object memory. + * + * @param object the specified object to be deleted. + */ +void rt_object_delete(rt_object_t object) +{ + register rt_base_t temp; + + /* object check */ + RT_ASSERT(object != RT_NULL); + RT_ASSERT(!(object->type & RT_Object_Class_Static)); + +#ifdef RT_USING_HOOK + if (rt_object_detach_hook != RT_NULL) rt_object_detach_hook(object); +#endif + + /* lock interrupt */ + temp = rt_hw_interrupt_disable(); + + /* remove from old list */ + rt_list_remove(&(object->list)); + + /* unlock interrupt */ + rt_hw_interrupt_enable(temp); + + /* free the memory of object */ + rt_free(object); +} +#endif + +/** + * This fucntion will find the object id by specified object name + * + * @param type the type of object + * @param name the specified object name + * + * @return object id for successful + */ +rt_object_t rt_object_find(enum rt_object_class_type type, const char* name) +{ + struct rt_object_information *information; + struct rt_object* object; + struct rt_list_node* node; + + information = &rt_object_container[type]; + + for (node = information->object_list.next; node != &(information->object_list); node = node->next) + { + object = rt_list_entry(node, struct rt_object, list); + if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0) + { + return object; + } + } + + /* not found */ + return RT_NULL; +} + +/** + * This function will judge the object is system object or not. + * Normally, the system object is a static object and the type + * of object set to RT_Object_Class_Static. + * + * @param object the specified object to be judged. + * + * @return RT_EOK if a system object, RT_ERROR for others. + */ +rt_err_t rt_object_is_systemobject(rt_object_t object) +{ + /* object check */ + RT_ASSERT(object != RT_NULL); + + if (object->type & RT_Object_Class_Static) return RT_EOK; + + return -RT_ERROR; +} + +/*@}*/ diff --git a/src/scheduler.c b/src/scheduler.c new file mode 100644 index 0000000000..907d0cdeba --- /dev/null +++ b/src/scheduler.c @@ -0,0 +1,414 @@ +/* + * File : scheduler.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-17 Bernard the first version + * 2006-04-28 Bernard fix the scheduler algorthm + * 2006-04-30 Bernard add SCHEDULER_DEBUG + * 2006-05-27 Bernard fix the scheduler algorthm for same priority thread + * schedule + * 2006-06-04 Bernard rewrite the scheduler algorithm + * 2006-08-03 Bernard add hook support + * 2006-09-05 Bernard add 32 priority level support + * 2006-09-24 Bernard add rt_system_scheduler_start function + */ + +#include <rtthread.h> +#include <rthw.h> + +#include "kservice.h" + +/* #define SCHEDULER_DEBUG */ + +static rt_int16_t rt_scheduler_lock_nest; +extern rt_uint32_t rt_interrupt_nest; + +rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX]; +struct rt_thread* rt_current_thread; + +rt_uint8_t rt_current_priority; + +#if RT_THREAD_PRIORITY_MAX > 32 +/* maximun priority level, 256 */ +rt_uint32_t rt_thread_ready_priority_group; +rt_uint8_t rt_thread_ready_table[32]; +#else +/* maximun priority level, 32 */ +rt_uint32_t rt_thread_ready_priority_group; +#endif + +#ifdef RT_USING_HEAP +rt_list_t rt_thread_defunct; +#endif + +const rt_uint8_t rt_lowest_bitmap[] = +{ + /* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + /* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +#ifdef RT_USING_HOOK +static void (*rt_scheduler_hook)(struct rt_thread* from, struct rt_thread* to); + +/** + * @addtogroup Hook + */ +/*@{*/ + +/** + * This function will set a hook function, which will be invoked when thread + * switch happens. + * + * @param hook the hook function + */ +void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to)) +{ + rt_scheduler_hook = hook; +} + +/*@}*/ +#endif + +#ifdef RT_USING_OVERFLOW_CHECK +static void _rt_scheduler_stack_check(struct rt_thread* thread) +{ + RT_ASSERT(thread != RT_NULL); + + if ((rt_uint32_t)thread->sp <= (rt_uint32_t)thread->stack_addr) + { + rt_uint32_t level; + + rt_kprintf("thread:%s stack overflow\n", thread->name); + level = rt_hw_interrupt_disable(); + while (level); + } + else if ((rt_uint32_t)thread->sp <= ((rt_uint32_t)thread->stack_addr + 32)) + { + rt_kprintf("warning: %s stack is close to end of stack address.\n", + thread->name); + } +} +#endif + +/** + * @ingroup SystemInit + * This function will init the system scheduler + * + */ +void rt_system_scheduler_init(void) +{ + register rt_base_t offset; + + rt_scheduler_lock_nest = 0; + + for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) + { + rt_list_init(&rt_thread_priority_table[offset]); + } + + rt_current_priority = RT_THREAD_PRIORITY_MAX - 1; + rt_current_thread = RT_NULL; + + /* init ready priority group */ + rt_thread_ready_priority_group = 0; + +#if RT_THREAD_PRIORITY_MAX > 32 + /* init ready table */ + rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table)); +#endif + +#ifdef RT_USING_HEAP + /* init thread defunct */ + rt_list_init(&rt_thread_defunct); +#endif +} + +/** + * This function will startup scheduler. It will select one thread + * with the highest priority level, then switch to it. + */ +void rt_system_scheduler_start() +{ + register rt_uint8_t number; + register rt_uint8_t highest_ready_priority; + + struct rt_thread *to_thread; + + /* find out the highest priority task */ + if (rt_thread_ready_priority_group & 0xff) + { + number = rt_lowest_bitmap[rt_thread_ready_priority_group & 0xff]; + } + else if (rt_thread_ready_priority_group & 0xff00) + { + number = rt_lowest_bitmap[(rt_thread_ready_priority_group >> 8) & 0xff] + 8; + } + else if (rt_thread_ready_priority_group & 0xff0000) + { + number = rt_lowest_bitmap[(rt_thread_ready_priority_group >> 16) & 0xff] + 16; + } + else + { + number = rt_lowest_bitmap[(rt_thread_ready_priority_group >> 24) & 0xff] + 24; + } + +#if RT_THREAD_PRIORITY_MAX > 32 + highest_ready_priority = (number << 3) + rt_lowest_bitmap[rt_thread_ready_table[number]]; +#else + highest_ready_priority = number; +#endif + + /* get switch to thread */ + to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, + struct rt_thread, tlist); + + rt_current_thread = to_thread; + + /* switch to new thread */ + rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp); + + /* never come back */ +} + +/** + * @addtogroup Thread + */ +/*@{*/ + +/** + * This function will perform one schedule. It will select one thread + * with the highest priority level, then switch to it. + */ +void rt_schedule() +{ + register rt_uint8_t number; + register rt_base_t level; + register rt_uint8_t highest_ready_priority; + + struct rt_thread *to_thread; + struct rt_thread *from_thread; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* check the scheduler is enabled or not */ + if(rt_scheduler_lock_nest == 0) + { + /* find out the highest priority task */ + if (rt_thread_ready_priority_group & 0xff) + { + number = rt_lowest_bitmap[rt_thread_ready_priority_group & 0xff]; + } + else if (rt_thread_ready_priority_group & 0xff00) + { + number = rt_lowest_bitmap[(rt_thread_ready_priority_group >> 8) & 0xff] + 8; + } + else if (rt_thread_ready_priority_group & 0xff0000) + { + number = rt_lowest_bitmap[(rt_thread_ready_priority_group >> 16) & 0xff] + 16; + } + else + { + number = rt_lowest_bitmap[(rt_thread_ready_priority_group >> 24) & 0xff] + 24; + } + +#if RT_THREAD_PRIORITY_MAX > 32 + highest_ready_priority = (number << 3) + rt_lowest_bitmap[rt_thread_ready_table[number]]; +#else + highest_ready_priority = number; +#endif + /* get switch to thread */ + to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, + struct rt_thread, tlist); + + /* if the destination thread is not the same as current thread */ + if (to_thread != rt_current_thread) + { + rt_current_priority = highest_ready_priority; + from_thread = rt_current_thread; + rt_current_thread = to_thread; + +#ifdef RT_USING_HOOK + if (rt_scheduler_hook != RT_NULL) rt_scheduler_hook(from_thread, to_thread); +#endif + + /* switch to new thread */ +#ifdef SCHEDULER_DEBUG + rt_kprintf("[%d]switch to priority#%d thread:%s\n", rt_interrupt_nest, + highest_ready_priority, to_thread->name); +#endif +#ifdef RT_USING_OVERFLOW_CHECK + _rt_scheduler_stack_check(to_thread); +#endif + + if (rt_interrupt_nest == 0) + { + rt_hw_context_switch((rt_uint32_t)&from_thread->sp, (rt_uint32_t)&to_thread->sp); + } + else + { +#ifdef SCHEDULER_DEBUG + rt_kprintf("switch in interrupt\n"); +#endif + rt_hw_context_switch_interrupt((rt_uint32_t)&from_thread->sp, + (rt_uint32_t)&to_thread->sp); + } + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} + +/** + * This function will insert a thread to system ready queue. The state of + * thread will be set as READY and remove from suspend queue. + * + * @param thread the thread to be inserted + * @note Please do not invoke this function in user application. + */ +void rt_schedule_insert_thread(struct rt_thread* thread) +{ + register rt_base_t temp; + + RT_ASSERT(thread != RT_NULL); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* change stat */ + thread->stat = RT_THREAD_READY; + + /* insert thread to ready list */ + rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), + &(thread->tlist)); + + /* set priority mask */ +#ifdef SCHEDULER_DEBUG +#if RT_THREAD_PRIORITY_MAX == 32 + rt_kprintf("insert thread, the priority: %d\n", thread->current_priority); +#else + rt_kprintf("insert thread, the priority: %d 0x%x %d\n", thread->number, thread->number_mask, thread->high_mask); +#endif +#endif + +#if RT_THREAD_PRIORITY_MAX > 32 + rt_thread_ready_table[thread->number] |= thread->high_mask; +#endif + rt_thread_ready_priority_group |= thread->number_mask; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); +} + +/** + * This function will remove a thread from system ready queue. + * + * @param thread the thread to be removed + * + * @note Please do not invoke this function in user application. + */ +void rt_schedule_remove_thread(struct rt_thread* thread) +{ + register rt_base_t temp; + + RT_ASSERT(thread != RT_NULL); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + +#ifdef SCHEDULER_DEBUG +#if RT_THREAD_PRIORITY_MAX == 32 + rt_kprintf("remove thread, the priority: %d\n", thread->current_priority); +#else + rt_kprintf("remove thread, the priority: %d 0x%x %d\n", thread->number, + thread->number_mask, thread->high_mask); +#endif +#endif + + /* remove thread from ready list */ + rt_list_remove(&(thread->tlist)); + if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) + { +#if RT_THREAD_PRIORITY_MAX > 32 + rt_thread_ready_table[thread->number] &= ~thread->high_mask; + if (rt_thread_ready_table[thread->number] == 0) + { + rt_thread_ready_priority_group &= ~thread->number_mask; + } +#else + rt_thread_ready_priority_group &= ~thread->number_mask; +#endif + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); +} + +/** + * This function will lock the thread scheduler. + */ +void rt_enter_critical() +{ + register rt_base_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + if (rt_scheduler_lock_nest < 255u) + rt_scheduler_lock_nest++; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); +} + +/** + * This function will unlock the thread scheduler. + */ +void rt_exit_critical() +{ + register rt_base_t level; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + rt_scheduler_lock_nest --; + + if (rt_scheduler_lock_nest <= 0) + { + rt_scheduler_lock_nest = 0; + /* enable interrupt */ + rt_hw_interrupt_enable(level); +
+ rt_schedule(); + } + else + { + /* enable interrupt */ + rt_hw_interrupt_enable(level); + } +} + +/*@}*/ + diff --git a/src/slab.c b/src/slab.c new file mode 100644 index 0000000000..59a1db7aa1 --- /dev/null +++ b/src/slab.c @@ -0,0 +1,815 @@ +/* + * File : slab.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2008, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rt-thread.org/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2008-07-12 Bernard the first version + */ + +/* + * KERN_SLABALLOC.C - Kernel SLAB memory allocator + * + * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon <[email protected]> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <rthw.h> +#include <rtthread.h> + +/* #define RT_SLAB_DEBUG */ + +#if defined (RT_USING_HEAP) && defined (RT_USING_SLAB) +#ifdef RT_USING_HOOK +static void (*rt_malloc_hook)(void *ptr, rt_size_t size); +static void (*rt_free_hook)(void *ptr); + +/** + * @addtogroup Hook + */ +/*@{*/ + +/** + * This function will set a hook function, which will be invoked when a memory + * block is allocated from heap memory. + * + * @param hook the hook function + */ +void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) +{ + rt_malloc_hook = hook; +} + +/** + * This function will set a hook function, which will be invoked when a memory + * block is released to heap memory. + * + * @param hook the hook function + */ +void rt_free_sethook(void (*hook)(void *ptr)) +{ + rt_free_hook = hook; +} + +/*@}*/ + +#endif + +/* + * slab allocator implementation + * + * A slab allocator reserves a ZONE for each chunk size, then lays the + * chunks out in an array within the zone. Allocation and deallocation + * is nearly instantanious, and fragmentation/overhead losses are limited + * to a fixed worst-case amount. + * + * The downside of this slab implementation is in the chunk size + * multiplied by the number of zones. ~80 zones * 128K = 10MB of VM per cpu. + * In a kernel implementation all this memory will be physical so + * the zone size is adjusted downward on machines with less physical + * memory. The upside is that overhead is bounded... this is the *worst* + * case overhead. + * + * Slab management is done on a per-cpu basis and no locking or mutexes + * are required, only a critical section. When one cpu frees memory + * belonging to another cpu's slab manager an asynchronous IPI message + * will be queued to execute the operation. In addition, both the + * high level slab allocator and the low level zone allocator optimize + * M_ZERO requests, and the slab allocator does not have to pre initialize + * the linked list of chunks. + * + * XXX Balancing is needed between cpus. Balance will be handled through + * asynchronous IPIs primarily by reassigning the z_Cpu ownership of chunks. + * + * XXX If we have to allocate a new zone and M_USE_RESERVE is set, use of + * the new zone should be restricted to M_USE_RESERVE requests only. + * + * Alloc Size Chunking Number of zones + * 0-127 8 16 + * 128-255 16 8 + * 256-511 32 8 + * 512-1023 64 8 + * 1024-2047 128 8 + * 2048-4095 256 8 + * 4096-8191 512 8 + * 8192-16383 1024 8 + * 16384-32767 2048 8 + * (if RT_MM_PAGE_SIZE is 4K the maximum zone allocation is 16383) + * + * Allocations >= zone_limit go directly to kmem. + * + * API REQUIREMENTS AND SIDE EFFECTS + * + * To operate as a drop-in replacement to the FreeBSD-4.x malloc() we + * have remained compatible with the following API requirements: + * + * + small power-of-2 sized allocations are power-of-2 aligned (kern_tty) + * + all power-of-2 sized allocations are power-of-2 aligned (twe) + * + malloc(0) is allowed and returns non-RT_NULL (ahc driver) + * + ability to allocate arbitrarily large chunks of memory + */ + +/* + * Chunk structure for free elements + */ +typedef struct slab_chunk +{ + struct slab_chunk *c_next; +} slab_chunk; + +/* + * The IN-BAND zone header is placed at the beginning of each zone. + */ +typedef struct slab_zone { + rt_int32_t z_magic; /* magic number for sanity check */ + rt_int32_t z_nfree; /* total free chunks / ualloc space in zone */ + rt_int32_t z_nmax; /* maximum free chunks */ + + struct slab_zone *z_next; /* zoneary[] link if z_nfree non-zero */ + rt_uint8_t *z_baseptr; /* pointer to start of chunk array */ + + rt_int32_t z_uindex; /* current initial allocation index */ + rt_int32_t z_chunksize; /* chunk size for validation */ + + rt_int32_t z_zoneindex; /* zone index */ + slab_chunk *z_freechunk; /* free chunk list */ +} slab_zone; + +#define ZALLOC_SLAB_MAGIC 0x51ab51ab +#define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */ +#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */ +#define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */ +#define NZONES 72 /* number of zones */ +#define ZONE_RELEASE_THRESH 2 /* threshold number of zones */ + +static slab_zone *zone_array[NZONES]; /* linked list of zones NFree > 0 */ +static slab_zone *zone_free; /* whole zones that have become free */ + +static int zone_free_cnt; +static int zone_size; +static int zone_limit; +static int zone_page_cnt; + +#ifdef RT_MEM_STATS +/* some statistical variable */ +static rt_uint32_t rt_mem_allocated = 0; +static rt_uint32_t rt_mem_zone = 0; +static rt_uint32_t rt_mem_page_allocated = 0; +#endif + +/* + * Misc constants. Note that allocations that are exact multiples of + * RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module. + */ +#define MIN_CHUNK_SIZE 8 /* in bytes */ +#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1) + +/* + * Array of descriptors that describe the contents of each page + */ +#define PAGE_TYPE_FREE 0x00 +#define PAGE_TYPE_SMALL 0x01 +#define PAGE_TYPE_LARGE 0x02 +struct memusage { + rt_uint32_t type:2 ; /* page type */ + rt_uint32_t size:30; /* pages allocated or offset from zone */ +}; +static struct memusage *memusage = RT_NULL; +#define btokup(addr) (&memusage[((rt_uint32_t)(addr) - heap_start) >> RT_MM_PAGE_BITS]) + +static rt_uint32_t heap_start, heap_end; + +/* page allocator */ +struct rt_page_head +{ + struct rt_page_head *next; /* next valid page */ + rt_size_t page; /* number of page */ + + /* dummy */ + char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_page_head*) + sizeof (rt_size_t))]; +}; +static struct rt_page_head *rt_page_list; + +static void *rt_page_alloc(rt_size_t npages) +{ + struct rt_page_head *b, *n; + struct rt_page_head **prev; + + RT_ASSERT(npages != 0); + + for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next)) + { + if (b->page > npages) + { + /* splite pages */ + n = b + npages; + n->next = b->next; + n->page = b->page - npages; + *prev = n; + break; + } + + if (b->page == npages) + { + /* this node fit, remove this node */ + *prev = b->next; + break; + } + } + + return b; +} + +static void rt_page_free(void *addr, rt_size_t npages) +{ + struct rt_page_head *b, *n; + struct rt_page_head **prev; + + RT_ASSERT(addr != RT_NULL); + RT_ASSERT((rt_uint32_t)addr % RT_MM_PAGE_SIZE == 0); + RT_ASSERT(npages != 0); + + n = (struct rt_page_head *)addr; + + for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next)) + { + RT_ASSERT(b->page > 0); + RT_ASSERT(b > n || b + b->page <= n); + + if (b + b->page == n) + { + if (b + (b->page += npages) == b->next) + { + b->page += b->next->page; + b->next = b->next->next; + } + + return; + } + + if (b == n + npages) + { + n->page = b->page + npages; + n->next = b->next; + *prev = n; + + return; + } + + if (b > n + npages) break; + } + + n->page = npages; + n->next = b; + *prev = n; +} + +/* + * Initialize the page allocator + */ +static void rt_page_init(void* addr, rt_size_t npages) +{ + RT_ASSERT(addr != RT_NULL); + RT_ASSERT(npages != 0); + + rt_page_list = RT_NULL; + rt_page_free(addr, npages); +} + +/** + * @ingroup SystemInit + * + * This function will init system heap + * + * @param begin_addr the beginning address of system page + * @param end_addr the end address of system page + * + */ +void rt_system_heap_init(void *begin_addr, void* end_addr) +{ + rt_uint32_t limsize, npages; + + /* align begin and end addr to page */ + heap_start = RT_ALIGN((rt_uint32_t)begin_addr, RT_MM_PAGE_SIZE); + heap_end = RT_ALIGN((rt_uint32_t)end_addr, RT_MM_PAGE_SIZE); + + limsize = heap_end - heap_start; + npages = limsize / RT_MM_PAGE_SIZE; + +#ifdef RT_SLAB_DEBUG + rt_kprintf("heap[0x%x - 0x%x], size 0x%x, 0x%x pages\n", heap_start, heap_end, limsize, npages); +#endif + + /* init pages */ + rt_page_init((void*)heap_start, npages); + + /* calculate zone size */ + zone_size = ZALLOC_MIN_ZONE_SIZE; + while (zone_size < ZALLOC_MAX_ZONE_SIZE && (zone_size << 1) < (limsize/1024)) + zone_size <<= 1; + + zone_limit = zone_size / 4; + if (zone_limit > ZALLOC_ZONE_LIMIT) zone_limit = ZALLOC_ZONE_LIMIT; + + zone_page_cnt = zone_size / RT_MM_PAGE_SIZE; + +#ifdef RT_SLAB_DEBUG + rt_kprintf("zone size 0x%x, zone page count 0x%x\n", zone_size, zone_page_cnt); +#endif + + /* allocate memusage array */ + limsize = npages * sizeof(struct memusage); + limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE); + memusage = rt_page_alloc(limsize/RT_MM_PAGE_SIZE); + +#ifdef RT_SLAB_DEBUG + rt_kprintf("memusage 0x%x, size 0x%x\n", (rt_uint32_t)memusage, limsize); +#endif +} + +/* + * Calculate the zone index for the allocation request size and set the + * allocation request size to that particular zone's chunk size. + */ +rt_inline int zoneindex(rt_uint32_t *bytes) +{ + rt_uint32_t n = (rt_uint32_t)*bytes; /* unsigned for shift opt */ + + if (n < 128) + { + *bytes = n = (n + 7) & ~7; + return(n / 8 - 1); /* 8 byte chunks, 16 zones */ + } + if (n < 256) + { + *bytes = n = (n + 15) & ~15; + return(n / 16 + 7); + } + if (n < 8192) + { + if (n < 512) + { + *bytes = n = (n + 31) & ~31; + return(n / 32 + 15); + } + if (n < 1024) + { + *bytes = n = (n + 63) & ~63; + return(n / 64 + 23); + } + if (n < 2048) + { + *bytes = n = (n + 127) & ~127; + return(n / 128 + 31); + } + if (n < 4096) + { + *bytes = n = (n + 255) & ~255; + return(n / 256 + 39); + } + *bytes = n = (n + 511) & ~511; + return(n / 512 + 47); + } + if (n < 16384) + { + *bytes = n = (n + 1023) & ~1023; + return(n / 1024 + 55); + } + + rt_kprintf("Unexpected byte count %d", n); + return 0; +} + +/** + * @addtogroup MM + */ + +/*@{*/ + +/** + * This function will allocate a block from system heap memory. + * - If the nbytes is less than zero, + * or + * - If there is no nbytes sized memory valid in system, + * the RT_NULL is returned. + * + * @param size the size of memory to be allocated + * + * @return the allocated memory + * + */ +void *rt_malloc(rt_size_t size) +{ + slab_zone *z; + rt_int32_t zi; + slab_chunk *chunk; + rt_base_t interrupt_level; + struct memusage *kup; + + /* zero size, return RT_NULL */ + if (size == 0) return RT_NULL; + + /* + * Handle large allocations directly. There should not be very many of + * these so performance is not a big issue. + */ + if (size >= zone_limit) + { + size = RT_ALIGN(size, RT_MM_PAGE_SIZE); + + chunk = rt_page_alloc(size >> RT_MM_PAGE_BITS); + if (chunk == RT_NULL) return RT_NULL; + + /* set kup */ + kup = btokup(chunk); + kup->type = PAGE_TYPE_LARGE; + kup->size = size >> RT_MM_PAGE_BITS; + +#ifdef RT_SLAB_DEBUG + rt_kprintf("malloc a large memory 0x%x, page cnt %d, kup %d\n", + size, + size >> RT_MM_PAGE_BITS, + ((rt_uint32_t)chunk - heap_start) >> RT_MM_PAGE_BITS); +#endif + + /* lock interrupt */ + interrupt_level = rt_hw_interrupt_disable(); + goto done; + } + + /* + * Attempt to allocate out of an existing zone. First try the free list, + * then allocate out of unallocated space. If we find a good zone move + * it to the head of the list so later allocations find it quickly + * (we might have thousands of zones in the list). + * + * Note: zoneindex() will panic of size is too large. + */ + zi = zoneindex(&size); + RT_ASSERT(zi < NZONES); + +#ifdef RT_SLAB_DEBUG + rt_kprintf("try to malloc 0x%x on zone: %d\n", size, zi); +#endif + + interrupt_level = rt_hw_interrupt_disable(); + if ((z = zone_array[zi]) != RT_NULL) + { + RT_ASSERT(z->z_nfree > 0); + + /* Remove us from the zone_array[] when we become empty */ + if (--z->z_nfree == 0) + { + zone_array[zi] = z->z_next; + z->z_next = RT_NULL; + } + + /* + * No chunks are available but nfree said we had some memory, so + * it must be available in the never-before-used-memory area + * governed by uindex. The consequences are very serious if our zone + * got corrupted so we use an explicit rt_kprintf rather then a KASSERT. + */ + if (z->z_uindex + 1 != z->z_nmax) + { + z->z_uindex = z->z_uindex + 1; + chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); + } + else + { + /* find on free chunk list */ + chunk = z->z_freechunk; + + /* remove this chunk from list */ + z->z_freechunk = z->z_freechunk->c_next; + } + + goto done; + } + + /* + * If all zones are exhausted we need to allocate a new zone for this + * index. + * + * At least one subsystem, the tty code (see CROUND) expects power-of-2 + * allocations to be power-of-2 aligned. We maintain compatibility by + * adjusting the base offset below. + */ + { + rt_int32_t off; + + if ((z = zone_free) != RT_NULL) + { + /* remove zone from free zone list */ + zone_free = z->z_next; + --zone_free_cnt; + } + else + { + /* allocate a zone from page */ + z = rt_page_alloc(zone_size / RT_MM_PAGE_SIZE); + if (z == RT_NULL) goto fail; + +#ifdef RT_SLAB_DEBUG + rt_kprintf("alloc a new zone: 0x%x\n", (rt_uint32_t)z); +#endif + + /* set message usage */ + for (off = 0, kup = btokup(z); off < zone_page_cnt; off ++) + { + kup->type = PAGE_TYPE_SMALL; + kup->size = off; + + kup ++; + } + } + + /* clear to zero */ + rt_memset(z, 0, sizeof(slab_zone)); + + /* offset of slab zone struct in zone */ + off = sizeof(slab_zone); + + /* + * Guarentee power-of-2 alignment for power-of-2-sized chunks. + * Otherwise just 8-byte align the data. + */ + if ((size | (size - 1)) + 1 == (size << 1)) + off = (off + size - 1) & ~(size - 1); + else + off = (off + MIN_CHUNK_MASK) & ~MIN_CHUNK_MASK; + + z->z_magic = ZALLOC_SLAB_MAGIC; + z->z_zoneindex = zi; + z->z_nmax = (zone_size - off) / size; + z->z_nfree = z->z_nmax - 1; + z->z_baseptr = (rt_uint8_t*)z + off; + z->z_uindex = 0; + z->z_chunksize = size; + + chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); + + /* link to zone array */ + z->z_next = zone_array[zi]; + zone_array[zi] = z; + } + +done: + rt_hw_interrupt_enable(interrupt_level); + +#ifdef RT_USING_HOOK + if (rt_malloc_hook != RT_NULL) rt_malloc_hook((char*)chunk, size); +#endif + + return chunk; + +fail: + rt_hw_interrupt_enable(interrupt_level); + return RT_NULL; +} + +/** + * This function will change the size of previously allocated memory block. + * + * @param ptr the previously allocated memory block + * @param size the new size of memory block + * + * @return the allocated memory + */ +void *rt_realloc(void *ptr, rt_size_t size) +{ + void *nptr; + slab_zone *z; + struct memusage *kup; + + if (ptr == RT_NULL) return rt_malloc(size); + if (size == 0) + { + rt_free(ptr); + return RT_NULL; + } + + /* + * Get the original allocation's zone. If the new request winds up + * using the same chunk size we do not have to do anything. + */ + kup = btokup((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK); + if (kup->type == PAGE_TYPE_LARGE) + { + rt_size_t osize; + + osize = kup->size << RT_MM_PAGE_BITS; + if ((nptr = rt_malloc(size)) == RT_NULL) return RT_NULL; + rt_memcpy(nptr, ptr, size > osize? osize : size); + rt_free(ptr); + + return nptr; + } + else if (kup->type == PAGE_TYPE_SMALL) + { + z = (slab_zone*)(((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK) - kup->size * RT_MM_PAGE_SIZE); + RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC); + + zoneindex(&size); + if (z->z_chunksize == size) return(ptr); /* same chunk */ + + /* + * Allocate memory for the new request size. Note that zoneindex has + * already adjusted the request size to the appropriate chunk size, which + * should optimize our bcopy(). Then copy and return the new pointer. + */ + if ((nptr = rt_malloc(size)) == RT_NULL) return RT_NULL; + + rt_memcpy(nptr, ptr, size > z->z_chunksize? z->z_chunksize : size); + rt_free(ptr); + + return nptr; + } + + return RT_NULL; +} + +/** + * This function will contiguously allocate enough space for count objects + * that are size bytes of memory each and returns a pointer to the allocated + * memory. + * + * The allocated memory is filled with bytes of value zero. + * + * @param count number of objects to allocate + * @param size size of the objects to allocate + * + * @return pointer to allocated memory / NULL pointer if there is an error + */ +void *rt_calloc(rt_size_t count, rt_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = rt_malloc(count * size); + + /* zero the memory */ + if (p) rt_memset(p, 0, count * size); + + return p; +} + +/** + * This function will release the previously allocated memory block by rt_malloc. + * The released memory block is taken back to system heap. + * + * @param ptr the address of memory which will be released + */ +void rt_free(void *ptr) +{ + slab_zone *z; + slab_chunk *chunk; + struct memusage *kup; + rt_base_t interrupt_level; + + /* free a RT_NULL pointer */ + if (ptr == RT_NULL) return ; + +#ifdef RT_USING_HOOK + if (rt_free_hook != RT_NULL) rt_free_hook(ptr); +#endif + + /* get memory usage */ +#ifdef RT_SLAB_DEBUG + rt_uint32 addr = ((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK); + rt_kprintf("free a memory 0x%x and align to 0x%x, kup index %d\n", + (rt_uint32_t)ptr, + (rt_uint32_t)addr, + ((rt_uint32_t)(addr) - heap_start) >> RT_MM_PAGE_BITS); +#endif + + kup = btokup((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK); + /* release large allocation */ + if (kup->type == PAGE_TYPE_LARGE) + { + rt_uint32_t size; + + /* clear page counter */ + interrupt_level = rt_hw_interrupt_disable(); + size = kup->size; + kup->size = 0; + rt_hw_interrupt_enable(interrupt_level); + +#ifdef RT_SLAB_DEBUG + rt_kprintf("free large memory block 0x%x, page count %d\n", (rt_uint32_t)ptr, size); +#endif + + /* free this page */ + rt_page_free(ptr, size); + return; + } + + /* zone case. get out zone. */ + z = (slab_zone*)(((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK) - kup->size * RT_MM_PAGE_SIZE); + RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC); + + interrupt_level = rt_hw_interrupt_disable(); + chunk = (slab_chunk*)ptr; + chunk->c_next = z->z_freechunk; + z->z_freechunk = chunk; + + /* + * Bump the number of free chunks. If it becomes non-zero the zone + * must be added back onto the appropriate list. + */ + if (z->z_nfree++ == 0) + { + z->z_next = zone_array[z->z_zoneindex]; + zone_array[z->z_zoneindex] = z; + } + + /* + * If the zone becomes totally free, and there are other zones we + * can allocate from, move this zone to the FreeZones list. Since + * this code can be called from an IPI callback, do *NOT* try to mess + * with kernel_map here. Hysteresis will be performed at malloc() time. + */ + if (z->z_nfree == z->z_nmax && + (z->z_next || zone_array[z->z_zoneindex] != z)) + { + slab_zone **pz; + +#ifdef RT_SLAB_DEBUG + rt_kprintf("free zone 0x%x\n", (rt_uint32_t)z, z->z_zoneindex); +#endif + + /* remove zone from zone array list */ + for (pz = &zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next) ; + *pz = z->z_next; + + /* reset zone */ + z->z_magic = -1; + + /* insert to free zone list */ + z->z_next = zone_free; + zone_free = z; + + ++zone_free_cnt; + + /* release zone to page allocator */ + if (zone_free_cnt > ZONE_RELEASE_THRESH) + { + register rt_base_t i; + + z = zone_free; + zone_free = z->z_next; + --zone_free_cnt; + + /* set message usage */ + for (i = 0, kup = btokup(z); i < zone_page_cnt; i ++) + { + kup->type = PAGE_TYPE_FREE; + kup->size = 0; + kup ++; + } + + /* release pages */ + rt_page_free(z, zone_size); + } + } + rt_hw_interrupt_enable(interrupt_level); +} + +/*@}*/ + +#endif diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000000..b8ab0fb641 --- /dev/null +++ b/src/thread.c @@ -0,0 +1,625 @@ +/* + * File : thread.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-28 Bernard first version + * 2006-04-29 Bernard implement thread timer + * 2006-04-30 Bernard add THREAD_DEBUG + * 2006-05-27 Bernard fix the rt_thread_yield bug + * 2006-06-03 Bernard fix the thread timer init bug + * 2006-08-10 Bernard fix the timer bug in thread_sleep + * 2006-09-03 Bernard change rt_timer_delete to rt_timer_detach + * 2006-09-03 Bernard implement rt_thread_detach + * 2008-02-16 Bernard fix the rt_thread_timeout bug + */ + +#include <rtthread.h> +#include <rthw.h> +#include "kservice.h" + +/*#define THREAD_DEBUG */ + +extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX]; +extern struct rt_thread* rt_current_thread; +extern rt_uint8_t rt_current_priority; + +#ifdef RT_USING_HEAP +extern rt_list_t rt_thread_defunct; +#endif + +static void rt_thread_exit(void); +void rt_thread_timeout(void* parameter); + +static rt_err_t _rt_thread_init(struct rt_thread* thread, + const char* name, + void (*entry)(void* parameter), void* parameter, + void* stack_start, rt_uint32_t stack_size, + rt_uint8_t priority, rt_uint32_t tick) +{ + /* set thread id and init thread list */ + thread->tid = thread; + rt_list_init(&(thread->tlist)); + + thread->entry = (void*)entry; + thread->parameter = parameter; + + /* stack init */ + thread->stack_addr = stack_start; + thread->stack_size = stack_size; + + /* init thread stack */ + rt_memset(thread->stack_addr, '#', thread->stack_size); + thread->sp = (void*)rt_hw_stack_init(thread->entry, thread->parameter, + (void *) ((char *)thread->stack_addr + thread->stack_size - 4), + (void*)rt_thread_exit); + + /* priority init */
+ RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); + thread->init_priority = priority; + thread->current_priority = priority; + + /* tick init */ + thread->init_tick = tick; + thread->remaining_tick = tick; + + /* error and flags */ + thread->error = RT_EOK; + thread->stat = RT_THREAD_INIT; + thread->flags = 0; + +#ifdef RT_USING_MODULE
+ /* init module parent */
+ thread->module_parent = RT_NULL;
+#endif + + /* init user data */
+ thread->user_data = 0;
+ + /* init thread timer */ + rt_timer_init(&(thread->thread_timer), + thread->name, + rt_thread_timeout, + thread, + 0, + RT_TIMER_FLAG_ONE_SHOT); + + return RT_EOK; +} + +/** + * @addtogroup Thread + */ + +/*@{*/ + +/** + * This function will init a thread, normally it's used to initialize a static thread object. + * + * @param thread the static thread object + * @param name the name of thread, which shall be unique + * @param entry the entry function of thread + * @param parameter the parameter of thread enter function + * @param stack_start the start address of thread stack + * @param stack_size the size of thread stack + * @param priority the priority of thread + * @param tick the time slice if there are same priority thread + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_init(struct rt_thread* thread, + const char* name, + void (*entry)(void* parameter), void* parameter, + void* stack_start, rt_uint32_t stack_size, + rt_uint8_t priority, rt_uint32_t tick) +{ + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(stack_start != RT_NULL); + + /* init thread object */ + rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name); + + return _rt_thread_init(thread, name, entry, parameter, + stack_start, stack_size, + priority, tick); +} + +#ifdef RT_USING_HEAP +/** + * This function will create a thread object and allocate thread object memory and stack. + * + * @param name the name of thread, which shall be unique + * @param entry the entry function of thread + * @param parameter the parameter of thread enter function + * @param stack_size the size of thread stack + * @param priority the priority of thread + * @param tick the time slice if there are same priority thread + * + * @return the created thread object + * + */ +rt_thread_t rt_thread_create (const char* name, + void (*entry)(void* parameter), void* parameter, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick) +{ + struct rt_thread* thread; + void* stack_start; + + thread = (struct rt_thread*) rt_object_allocate(RT_Object_Class_Thread, name); + if (thread == RT_NULL) return RT_NULL; + + stack_start = (void*)rt_malloc(stack_size); + if (stack_start == RT_NULL) + { + /* allocate stack failure */ + rt_object_delete((rt_object_t)thread); + return RT_NULL; + } + + _rt_thread_init(thread, name, entry, parameter, + stack_start, stack_size, + priority, tick); + + return thread; +} +#endif + +/** + * This function will return self thread object + * + * @return the self thread object + * + */ +rt_thread_t rt_thread_self (void) +{ + return rt_current_thread; +} + +/** + * This function will start a thread and put it to system ready queue + * + * @param thread the thread to be started + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_startup (rt_thread_t thread) +{ + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(thread->stat == RT_THREAD_INIT); + + /* set current priority to init priority */ + thread->current_priority = thread->init_priority; + + /* calculate priority attribute */ +#if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ +#else + thread->number_mask = 1 << thread->current_priority; +#endif + +#ifdef THREAD_DEBUG + rt_kprintf("startup a thread:%s with priority:%d\n", thread->name, thread->init_priority); +#endif + + /* change thread stat */ + thread->stat = RT_THREAD_SUSPEND; + /* then resume it */ + rt_thread_resume(thread); + + return RT_EOK; +} + +static void rt_thread_exit() +{ + struct rt_thread* thread; + register rt_base_t temp; + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* get current thread */ + thread = rt_current_thread; + + /* remove from schedule */ + rt_schedule_remove_thread(thread); + + /* change stat */ + thread->stat = RT_THREAD_CLOSE; + + /* release thread timer */ + rt_timer_detach(&(thread->thread_timer)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + if (rt_object_is_systemobject((rt_object_t)thread) == RT_EOK) + { + rt_object_detach((rt_object_t)thread); + } +#ifdef RT_USING_HEAP + else + { + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* insert to defunct thread list */ + rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + } +#endif + + /* switch to next task */ + rt_schedule(); +} + +/** + * This function will detach a thread. The thread object will be remove from thread + * queue and detached/deleted from system object management. + * + * @param thread the thread to be deleted + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_detach (rt_thread_t thread) +{ + /* thread check */ + RT_ASSERT(thread != RT_NULL); + + /* remove from schedule */ + rt_schedule_remove_thread(thread); + + /* release thread timer */ + rt_timer_detach(&(thread->thread_timer)); + + rt_object_detach((rt_object_t)thread); + + return RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will delete a thread. The thread object will be remove from thread + * queue and detached/deleted from system object management. + * + * @param thread the thread to be deleted + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_delete (rt_thread_t thread) +{ + rt_base_t lock; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + + /* remove from schedule */ + rt_schedule_remove_thread(thread); + + /* release thread timer */ + rt_timer_detach(&(thread->thread_timer)); + + /* change stat */ + thread->stat = RT_THREAD_CLOSE; + + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); + + /* insert to defunct thread list */ + rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); + + /* enable interrupt */ + rt_hw_interrupt_enable(lock); + + return RT_EOK; +} +#endif + +/** + * This function will let current thread yield processor, and scheduler will get a highest thread to run. + * After yield processor, the current thread is still in READY state. + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_yield () +{ + register rt_base_t level; + struct rt_thread *thread; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* set to current thread */ + thread = rt_current_thread; + + /* if the thread stat is READY and on ready queue list */ + if (thread->stat == RT_THREAD_READY && thread->tlist.next != thread->tlist.prev) + { + /* remove thread from thread list */ + rt_list_remove(&(thread->tlist)); + + /* put thread to end of ready queue */ + rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), + &(thread->tlist)); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +/** + * This function will let current thread sleep for some ticks. + * + * @param tick the sleep ticks + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_thread_sleep (rt_tick_t tick) +{ + register rt_base_t temp; + struct rt_thread *thread; + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + /* set to current thread */ + thread = rt_current_thread; + RT_ASSERT(thread != RT_NULL); + + /* suspend thread */ + rt_thread_suspend(thread); + + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&(thread->thread_timer)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; +} + +/** + * This function will let current thread delay for some ticks. + * + * @param tick the delay ticks + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_thread_delay(rt_tick_t tick) +{ + return rt_thread_sleep(tick); +} + +rt_err_t rt_thread_control (rt_thread_t thread, rt_uint8_t cmd, void* arg) +{ + register rt_base_t temp; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + + switch (cmd) + { + case RT_THREAD_CTRL_CHANGE_PRIORITY: + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* for ready thread, change queue */ + if (thread->stat == RT_THREAD_READY) + { + /* remove thread from schedule queue first */ + rt_schedule_remove_thread(thread); + + /* change thread priority */ + thread->current_priority = *(rt_uint8_t*) arg; + + /* recalculate priority attribute */ +#if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ +#else + thread->number_mask = 1 << thread->current_priority; +#endif + + /* insert thread to schedule queue again */ + rt_schedule_insert_thread(thread); + } + else + { + thread->current_priority = *(rt_uint8_t*) arg; + + /* recalculate priority attribute */ +#if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ +#else + thread->number_mask = 1 << thread->current_priority; +#endif + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + break; + + case RT_THREAD_CTRL_STARTUP: + return rt_thread_startup(thread); + +#ifdef RT_USING_HEAP + case RT_THREAD_CTRL_CLOSE: + return rt_thread_delete(thread); +#endif + + default: + break; + } + + return - RT_EOK; +} + +/** + * This function will suspend the specified thread. + * + * @param thread the thread to be suspended + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_suspend (rt_thread_t thread) +{ + register rt_base_t temp; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + +#ifdef THREAD_DEBUG + rt_kprintf("thread suspend: %s\n", thread->name); +#endif + + if (thread->stat != RT_THREAD_READY) + { +#ifdef THREAD_DEBUG + rt_kprintf("thread suspend: thread disorder, %d\n", thread->stat); +#endif + return -RT_ERROR; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* change thread stat */ + thread->stat = RT_THREAD_SUSPEND; + rt_schedule_remove_thread(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +/** + * This function will resume a thread and put it to system ready queue. + * + * @param thread the thread to be resumed + * + * @return the operation status, RT_EOK on OK; -RT_ERROR on error + * + */ +rt_err_t rt_thread_resume (rt_thread_t thread) +{ + register rt_base_t temp; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + +#ifdef THREAD_DEBUG + rt_kprintf("thread resume: %s\n", thread->name); +#endif + + if (thread->stat != RT_THREAD_SUSPEND) + { +#ifdef THREAD_DEBUG + rt_kprintf("thread resume: thread disorder, %d\n", thread->stat); +#endif + return -RT_ERROR; + } + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* remove from suspend list */ + rt_list_remove(&(thread->tlist)); + + /* remove thread timer */ + rt_list_remove(&(thread->thread_timer.list)); + + /* change timer state */ + thread->thread_timer.parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); + + return RT_EOK; +} + +/** + * This function is the timeout function for thread, normally which will + * be invoked when thread is timeout to wait some recourse. + * + * @param parameter the parameter of thread timeout function + * + */ +void rt_thread_timeout(void* parameter) +{ + struct rt_thread* thread; + + thread = (struct rt_thread*) parameter; + + /* thread check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT(thread->stat == RT_THREAD_SUSPEND); + + /* set error number */ + thread->error = -RT_ETIMEOUT; + + /* remove from suspend list */ + rt_list_remove(&(thread->tlist)); + + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); + + /* do schedule */ + rt_schedule(); +} + +/** + * This function will find the specified thread. + * + * @param name the name of thread finding + * + * @return the thread + */ +rt_thread_t rt_thread_find(char* name) +{ + struct rt_thread* thread; + + thread = (struct rt_thread*)rt_object_find(RT_Object_Class_Thread, name); + + return thread; +} + +/*@}*/ diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000000..558cbfac35 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,384 @@ +/* + * File : timer.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://openlab.rt-thread.com/license/LICENSE + * + * Change Logs: + * Date Author Notes + * 2006-03-12 Bernard first version + * 2006-04-29 Bernard implement thread timer + * 2006-06-04 Bernard implement rt_timer_control + * 2006-08-10 Bernard fix the periodic timer bug + * 2006-09-03 Bernard implement rt_timer_detach + */ + +#include <rtthread.h> +#include <rthw.h> + +#include "kservice.h" + +/* #define TIMER_DEBUG */ +static rt_list_t rt_timer_list; + +#ifdef RT_USING_HOOK +extern void (*rt_object_take_hook)(struct rt_object* object); +extern void (*rt_object_put_hook)(struct rt_object* object); +static void (*rt_timer_timeout_hook)(struct rt_timer* timer); + +/** + * @addtogroup Hook + */ +/*@{*/ + +/** + * This function will set a hook function, which will be invoked when timer + * is timeout. + * + * @param hook the hook function + */ +void rt_timer_timeout_sethook(void (*hook)(struct rt_timer* timer)) +{ + rt_timer_timeout_hook = hook; +} + +/*@}*/ +#endif + +/** + * @ingroup SystemInit + * + * This function will init system timer + * + */ +void rt_system_timer_init() +{ + rt_list_init(&rt_timer_list); +} + +static void _rt_timer_init(rt_timer_t timer, + void (*timeout)(void* parameter), void* parameter, + rt_tick_t time, rt_uint8_t flag) +{ + /* set flag */ + timer->parent.flag = flag; + + /* set deactivated */ + timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + timer->timeout_func = timeout; + timer->parameter = parameter; + + timer->timeout_tick = 0; + timer->init_tick = time; + + /* init timer list */ + rt_list_init(&(timer->list)); +} + +/** + * @addtogroup Clock + */ +/*@{*/ + +/** + * This function will init a timer, normally this function is used to initialize + * a static timer object. + * + * @param timer the static timer object + * @param name the name of timer + * @param timeout the timeout function + * @param parameter the parameter of timeout function + * @param time the tick of timer + * @param flag the flag of timer + */ +void rt_timer_init(rt_timer_t timer, + const char* name, + void (*timeout)(void* parameter), void* parameter, + rt_tick_t time, rt_uint8_t flag) +{ + /* timer check */ + RT_ASSERT(timer != RT_NULL); + + /* timer object init */ + rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name); + + _rt_timer_init(timer, timeout, parameter, time, flag); +} + +/** + * This function will detach a timer from timer management. + * + * @param timer the static timer object + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + */ +rt_err_t rt_timer_detach(rt_timer_t timer) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* remove it from timer list */ + rt_list_remove(&(timer->list)); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_object_detach((rt_object_t)timer); + + return -RT_EOK; +} + +#ifdef RT_USING_HEAP +/** + * This function will create a timer + * + * @param name the name of timer + * @param timeout the timeout function + * @param parameter the parameter of timeout function + * @param time the tick of timer + * @param flag the flag of timer + * + * @return the created timer object + */ +rt_timer_t rt_timer_create(const char* name, void (*timeout)(void* parameter), void* parameter, rt_tick_t time, rt_uint8_t flag) +{ + struct rt_timer* timer; + + /* allocate a object */ + timer = (struct rt_timer*)rt_object_allocate(RT_Object_Class_Timer, name); + if (timer == RT_NULL) + { + return RT_NULL; + } + + _rt_timer_init(timer, timeout, parameter, time, flag); + + return timer; +} + +/** + * This function will delete a timer and release timer memory + * + * @param timer the timer to be deleted + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_timer_delete(rt_timer_t timer) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* remove it from timer list */ + rt_list_remove(&(timer->list)); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + rt_object_delete((rt_object_t)timer); + + return -RT_EOK; +} +#endif + +/** + * This function will start the timer + * + * @param timer the timer to be started + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_timer_start(rt_timer_t timer) +{ + rt_list_t *n; + struct rt_timer* t; + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) return -RT_ERROR; + +#ifdef RT_USING_HOOK + if (rt_object_take_hook != RT_NULL) rt_object_take_hook(&(timer->parent)); +#endif + + timer->timeout_tick = rt_tick_get() + timer->init_tick; + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* insert timer to system timer list */ + for (n = rt_timer_list.next; n != &rt_timer_list; n = n->next) + { + t = rt_list_entry(n, struct rt_timer, list); + if (t->timeout_tick > timer->timeout_tick) + { + rt_list_insert_before(n, &(timer->list)); + break; + } + } + + /* no found suitable position in timer list */ + if (n == &rt_timer_list) + { + rt_list_insert_before(n, &(timer->list)); + } + + timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return -RT_EOK; +} + +/** + * This function will stop the timer + * + * @param timer the timer to be stopped + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_timer_stop(rt_timer_t timer) +{ + register rt_base_t level; + + /* timer check */ + RT_ASSERT(timer != RT_NULL); + if(!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) return -RT_ERROR; + +#ifdef RT_USING_HOOK + if (rt_object_put_hook != RT_NULL) rt_object_put_hook(&(timer->parent)); +#endif + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + /* remove it from timer list */ + rt_list_remove(&(timer->list)); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +/** + * This function will get or set some options of the timer + * + * @param timer the timer to be get or set + * @param cmd the control command + * @param arg the argument + * + * @return the operation status, RT_EOK on OK; RT_ERROR on error + * + */ +rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void* arg) +{ + /* timer check */ + RT_ASSERT(timer != RT_NULL); + + switch (cmd) + { + case RT_TIMER_CTRL_GET_TIME: + *(rt_tick_t*)arg = timer->init_tick; + break; + + case RT_TIMER_CTRL_SET_TIME: + timer->init_tick = *(rt_tick_t*)arg; + break; + + case RT_TIMER_CTRL_SET_ONESHOT: + timer->parent.flag &= ~(1 << RT_TIMER_FLAG_PERIODIC); + break; + + case RT_TIMER_CTRL_SET_PERIODIC: + timer->parent.flag |= (1 << RT_TIMER_FLAG_PERIODIC); + break; + } + + return RT_EOK; +} + +/** + * This function will check timer list, if a timeout event happens, the + * corresponding timeout function will be invoked. + * + */ +void rt_timer_check() +{ + rt_tick_t current_tick; + rt_list_t *n; + struct rt_timer *t; + register rt_base_t level; + +#ifdef TIMER_DEBUG + rt_kprintf("timer check enter\n"); +#endif + + current_tick = rt_tick_get(); + + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + for (n = rt_timer_list.next; n != &(rt_timer_list); ) + { + t = rt_list_entry(n, struct rt_timer, list); + if (current_tick >= t->timeout_tick) + { +#ifdef RT_USING_HOOK + if (rt_timer_timeout_hook != RT_NULL) rt_timer_timeout_hook(t); +#endif + /* move node to the next */ + n = n->next; + + /* remove timer from timer list firstly */ + rt_list_remove(&(t->list)); + + /* call timeout function */ + t->timeout_func(t->parameter); + + /* reget tick */ + current_tick = rt_tick_get(); + +#ifdef TIMER_DEBUG + rt_kprintf("current tick: %d\n", current_tick); +#endif + + /* change timer state */ + t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + + if (t->parent.flag & RT_TIMER_FLAG_PERIODIC) + { + /* start it */ + rt_timer_start(t); + } + } + else break; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + +#ifdef TIMER_DEBUG + rt_kprintf("timer check leave\n"); +#endif +} + +/*@}*/ |
