1、【增加】RT-Thread 系统 Demo。

Signed-off-by: armink <armink.ztl@gmail.com>
This commit is contained in:
armink
2017-04-15 15:14:49 +08:00
parent cee09e4a57
commit b536a063d6
247 changed files with 137359 additions and 2 deletions
@@ -0,0 +1,35 @@
Import('RTT_ROOT')
Import('rtconfig')
from building import *
src = Glob('*.c')
CPPPATH = [RTT_ROOT + '/include']
if rtconfig.CROSS_TOOL == 'keil' and GetDepend('RT_USING_MODULE') == True:
LINKFLAGS = ' --keep __rtmsym_* '
else:
LINKFLAGS = ''
if GetDepend('RT_USING_MODULE') == False:
SrcRemove(src, ['module.c'])
if GetDepend('RT_USING_HEAP') == False or GetDepend('RT_USING_SMALL_MEM') == False:
SrcRemove(src, ['mem.c'])
if GetDepend('RT_USING_HEAP') == False or GetDepend('RT_USING_SLAB') == False:
SrcRemove(src, ['slab.c'])
if GetDepend('RT_USING_MEMPOOL') == False:
SrcRemove(src, ['mempool.c'])
if GetDepend('RT_USING_MEMHEAP') == False:
SrcRemove(src, ['memheap.c'])
if GetDepend('RT_USING_MEMHEAP_AS_HEAP'):
SrcRemove(src, ['mem.c'])
if GetDepend('RT_USING_DEVICE') == False:
SrcRemove(src, ['device.c'])
group = DefineGroup('Kernel', src, depend = [''], CPPPATH = CPPPATH, LINKFLAGS = LINKFLAGS)
Return('group')
@@ -0,0 +1,122 @@
/*
* File : clock.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-03-12 Bernard first version
* 2006-05-27 Bernard add support for same priority thread schedule
* 2006-08-10 Bernard remove the last rt_schedule in rt_tick_increase
* 2010-03-08 Bernard remove rt_passed_second
* 2010-05-20 Bernard fix the tick exceeds the maximum limits
* 2010-07-13 Bernard fix rt_tick_from_millisecond issue found by kuronca
* 2011-06-26 Bernard add rt_tick_set function.
*/
#include <rthw.h>
#include <rtthread.h>
static rt_tick_t rt_tick = 0;
extern void rt_timer_check(void);
/**
* This function will init system tick and set it to zero.
* @ingroup SystemInit
*
* @deprecated since 1.1.0, this function does not need to be invoked
* in the system initialization.
*/
void rt_system_tick_init(void)
{
}
/**
* @addtogroup Clock
*/
/*@{*/
/**
* This function will return current tick from operating system startup
*
* @return current tick
*/
rt_tick_t rt_tick_get(void)
{
/* return the global tick */
return rt_tick;
}
RTM_EXPORT(rt_tick_get);
/**
* This function will set current tick
*/
void rt_tick_set(rt_tick_t tick)
{
rt_base_t level;
level = rt_hw_interrupt_disable();
rt_tick = tick;
rt_hw_interrupt_enable(level);
}
/**
* This function will notify kernel there is one tick passed. Normally,
* this function is invoked by clock ISR.
*/
void rt_tick_increase(void)
{
struct rt_thread *thread;
/* increase the global tick */
++ rt_tick;
/* 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();
}
/* check timer */
rt_timer_check();
}
/**
* 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 + 999) / 1000;
}
RTM_EXPORT(rt_tick_from_millisecond);
/*@}*/
@@ -0,0 +1,70 @@
#include <rtthread.h>
#include <rthw.h>
#define CPU_USAGE_CALC_TICK 10
#define CPU_USAGE_LOOP 100
static rt_uint8_t cpu_usage_major = 0, cpu_usage_minor= 0;
static rt_uint32_t total_count = 0;
static void cpu_usage_idle_hook()
{
rt_tick_t tick;
rt_uint32_t count;
volatile rt_uint32_t loop;
if (total_count == 0)
{
/* get total count */
rt_enter_critical();
tick = rt_tick_get();
while(rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
{
total_count ++;
loop = 0;
while (loop < CPU_USAGE_LOOP) loop ++;
}
rt_exit_critical();
}
count = 0;
/* get CPU usage */
tick = rt_tick_get();
while (rt_tick_get() - tick < CPU_USAGE_CALC_TICK)
{
count ++;
loop = 0;
while (loop < CPU_USAGE_LOOP) loop ++;
}
/* calculate major and minor */
if (count < total_count)
{
count = total_count - count;
cpu_usage_major = (count * 100) / total_count;
cpu_usage_minor = ((count * 100) % total_count) * 100 / total_count;
}
else
{
total_count = count;
/* no CPU usage */
cpu_usage_major = 0;
cpu_usage_minor = 0;
}
}
void cpu_usage_get(rt_uint8_t *major, rt_uint8_t *minor)
{
RT_ASSERT(major != RT_NULL);
RT_ASSERT(minor != RT_NULL);
*major = cpu_usage_major;
*minor = cpu_usage_minor;
}
void cpu_usage_init()
{
/* set idle thread hook */
rt_thread_idle_sethook(cpu_usage_idle_hook);
}
@@ -0,0 +1,439 @@
/*
* File : device.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2013, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2007-01-21 Bernard the first version
* 2010-05-04 Bernard add rt_device_init implementation
* 2012-10-20 Bernard add device check in register function,
* provided by Rob <rdent@iinet.net.au>
* 2012-12-25 Bernard return RT_EOK if the device interface not exist.
* 2013-07-09 Grissiom add ref_count support
*/
#include <rtthread.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;
if (rt_device_find(name) != RT_NULL)
return -RT_ERROR;
rt_object_init(&(dev->parent), RT_Object_Class_Device, name);
dev->flag = flags;
dev->ref_count = 0;
return RT_EOK;
}
RTM_EXPORT(rt_device_register);
/**
* This function removes a previously registered device driver
*
* @param dev the pointer of device driver structure
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_device_unregister(rt_device_t dev)
{
RT_ASSERT(dev != RT_NULL);
rt_object_detach(&(dev->parent));
return RT_EOK;
}
RTM_EXPORT(rt_device_unregister);
/**
* This function initializes all registered device driver
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_device_init_all(void)
{
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("To initialize device:%s failed. The error code is %d\n",
device->parent.name, result);
}
else
{
device->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
}
}
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)
{
struct rt_object *object;
struct rt_list_node *node;
struct rt_object_information *information;
extern struct rt_object_information rt_object_container[];
/* enter critical */
if (rt_thread_self() != RT_NULL)
rt_enter_critical();
/* try to find device object */
information = &rt_object_container[RT_Object_Class_Device];
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)
{
/* leave critical */
if (rt_thread_self() != RT_NULL)
rt_exit_critical();
return (rt_device_t)object;
}
}
/* leave critical */
if (rt_thread_self() != RT_NULL)
rt_exit_critical();
/* not found */
return RT_NULL;
}
RTM_EXPORT(rt_device_find);
/**
* This function will initialize the specified device
*
* @param dev the pointer of device driver structure
*
* @return the result
*/
rt_err_t rt_device_init(rt_device_t dev)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev != RT_NULL);
/* get device init handler */
if (dev->init != RT_NULL)
{
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
result = dev->init(dev);
if (result != RT_EOK)
{
rt_kprintf("To initialize device:%s failed. The error code is %d\n",
dev->parent.name, result);
}
else
{
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
}
}
return result;
}
/**
* This function will open a device
*
* @param dev the pointer of device driver structure
* @param oflag the flags for device open
*
* @return the result
*/
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev != RT_NULL);
/* if device is not initialized, initialize it. */
if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
{
if (dev->init != RT_NULL)
{
result = dev->init(dev);
if (result != RT_EOK)
{
rt_kprintf("To initialize device:%s failed. The error code is %d\n",
dev->parent.name, result);
return result;
}
}
dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
}
/* device is a stand alone device and opened */
if ((dev->flag & RT_DEVICE_FLAG_STANDALONE) &&
(dev->open_flag & RT_DEVICE_OFLAG_OPEN))
{
return -RT_EBUSY;
}
/* call device open interface */
if (dev->open != RT_NULL)
{
result = dev->open(dev, oflag);
}
/* set open flag */
if (result == RT_EOK || result == -RT_ENOSYS)
{
dev->open_flag = oflag | RT_DEVICE_OFLAG_OPEN;
dev->ref_count++;
/* don't let bad things happen silently. If you are bitten by this assert,
* please set the ref_count to a bigger type. */
RT_ASSERT(dev->ref_count != 0);
}
return result;
}
RTM_EXPORT(rt_device_open);
/**
* This function will close a device
*
* @param dev the pointer of device driver structure
*
* @return the result
*/
rt_err_t rt_device_close(rt_device_t dev)
{
rt_err_t result = RT_EOK;
RT_ASSERT(dev != RT_NULL);
if (dev->ref_count == 0)
return -RT_ERROR;
dev->ref_count--;
if (dev->ref_count != 0)
return RT_EOK;
/* call device close interface */
if (dev->close != RT_NULL)
{
result = dev->close(dev);
}
/* set open flag */
if (result == RT_EOK || result == -RT_ENOSYS)
dev->open_flag = RT_DEVICE_OFLAG_CLOSE;
return result;
}
RTM_EXPORT(rt_device_close);
/**
* This function will read some data from a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of reading
* @param buffer the data buffer to save read data
* @param size the size of buffer
*
* @return the actually read size on successful, otherwise negative returned.
*
* @note since 0.4.0, the unit of size/pos is a block for block device.
*/
rt_size_t rt_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
RT_ASSERT(dev != RT_NULL);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device read interface */
if (dev->read != RT_NULL)
{
return dev->read(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
RTM_EXPORT(rt_device_read);
/**
* This function will write some data to a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of written
* @param buffer the data buffer to be written to device
* @param size the size of buffer
*
* @return the actually written size on successful, otherwise negative returned.
*
* @note since 0.4.0, the unit of size/pos is a block for block device.
*/
rt_size_t rt_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
RT_ASSERT(dev != RT_NULL);
if (dev->ref_count == 0)
{
rt_set_errno(-RT_ERROR);
return 0;
}
/* call device write interface */
if (dev->write != RT_NULL)
{
return dev->write(dev, pos, buffer, size);
}
/* set error code */
rt_set_errno(-RT_ENOSYS);
return 0;
}
RTM_EXPORT(rt_device_write);
/**
* This function will perform a variety of control functions on devices.
*
* @param dev the pointer of device driver structure
* @param cmd the command sent to device
* @param arg the argument of command
*
* @return the result
*/
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void *arg)
{
RT_ASSERT(dev != RT_NULL);
/* call device write interface */
if (dev->control != RT_NULL)
{
return dev->control(dev, cmd, arg);
}
return RT_EOK;
}
RTM_EXPORT(rt_device_control);
/**
* This function will set the indication callback function when device receives
* data.
*
* @param dev the pointer of device driver structure
* @param rx_ind the indication callback function
*
* @return RT_EOK
*/
rt_err_t
rt_device_set_rx_indicate(rt_device_t dev,
rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size))
{
RT_ASSERT(dev != RT_NULL);
dev->rx_indicate = rx_ind;
return RT_EOK;
}
RTM_EXPORT(rt_device_set_rx_indicate);
/**
* This function will set the indication callback function when device has
* written data to physical hardware.
*
* @param dev the pointer of device driver structure
* @param tx_done the indication callback function
*
* @return RT_EOK
*/
rt_err_t
rt_device_set_tx_complete(rt_device_t dev,
rt_err_t (*tx_done)(rt_device_t dev, void *buffer))
{
RT_ASSERT(dev != RT_NULL);
dev->tx_complete = tx_done;
return RT_EOK;
}
RTM_EXPORT(rt_device_set_tx_complete);
#endif
@@ -0,0 +1,203 @@
/*
* File : idle.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-03-23 Bernard the first version
* 2010-11-10 Bernard add cleanup callback function in thread exit.
* 2012-12-29 Bernard fix compiling warning.
* 2013-12-21 Grissiom let rt_thread_idle_excute loop until there is no
* dead thread.
*/
#include <rthw.h>
#include <rtthread.h>
#ifndef IDLE_THREAD_STACK_SIZE
#if defined (RT_USING_HOOK) || defined(RT_USING_HEAP)
#define IDLE_THREAD_STACK_SIZE 256
#else
#define IDLE_THREAD_STACK_SIZE 128
#endif
#endif
static struct rt_thread idle;
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_thread_stack[IDLE_THREAD_STACK_SIZE];
extern rt_list_t rt_thread_defunct;
#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)(void))
{
rt_thread_idle_hook = hook;
}
/*@}*/
#endif
/**
* @ingroup Thread
*
* This function will perform system background job when system idle.
*/
void rt_thread_idle_excute(void)
{
/* Loop until there is no dead thread. So one call to rt_thread_idle_excute
* will do all the cleanups. */
while (!rt_list_isempty(&rt_thread_defunct))
{
rt_base_t lock;
rt_thread_t thread;
#ifdef RT_USING_MODULE
rt_module_t module = RT_NULL;
#endif
RT_DEBUG_NOT_IN_INTERRUPT;
/* disable interrupt */
lock = rt_hw_interrupt_disable();
/* re-check whether list is empty */
if (!rt_list_isempty(&rt_thread_defunct))
{
/* get defunct thread */
thread = rt_list_entry(rt_thread_defunct.next,
struct rt_thread,
tlist);
#ifdef RT_USING_MODULE
/* get thread's parent module */
module = (rt_module_t)thread->module_id;
/* if the thread is module's main thread */
if (module != RT_NULL && module->module_thread == thread)
{
/* detach module's main thread */
module->module_thread = RT_NULL;
}
#endif
/* remove defunct thread */
rt_list_remove(&(thread->tlist));
/* invoke thread cleanup */
if (thread->cleanup != RT_NULL)
thread->cleanup(thread);
/* if it's a system object, not delete it */
if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE)
{
/* enable interrupt */
rt_hw_interrupt_enable(lock);
return;
}
}
else
{
/* enable interrupt */
rt_hw_interrupt_enable(lock);
/* may the defunct thread list is removed by others, just return */
return;
}
/* enable interrupt */
rt_hw_interrupt_enable(lock);
#ifdef RT_USING_HEAP
#if defined(RT_USING_MODULE) && defined(RT_USING_SLAB)
/* the thread belongs to an application module */
if (thread->flags & RT_OBJECT_FLAG_MODULE)
rt_module_free((rt_module_t)thread->module_id, thread->stack_addr);
else
#endif
/* release thread's stack */
RT_KERNEL_FREE(thread->stack_addr);
/* delete thread object */
rt_object_delete((rt_object_t)thread);
#endif
#ifdef RT_USING_MODULE
if (module != RT_NULL)
{
extern rt_err_t rt_module_destroy(rt_module_t module);
/* if sub thread list and main thread are all empty */
if ((module->module_thread == RT_NULL) &&
rt_list_isempty(&module->module_object[RT_Object_Class_Thread].object_list))
{
module->nref --;
}
/* destroy module */
if (module->nref == 0)
rt_module_destroy(module);
}
#endif
}
}
static void rt_thread_idle_entry(void *parameter)
{
while (1)
{
#ifdef RT_USING_HOOK
if (rt_thread_idle_hook != RT_NULL)
rt_thread_idle_hook();
#endif
rt_thread_idle_excute();
}
}
/**
* @ingroup SymstemInit
*
* This function will initialize idle thread, then start it.
*
* @note this function must be invoked when system init.
*/
void rt_thread_idle_init(void)
{
/* initialize 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);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,97 @@
/*
* File : irq.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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_uint8_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(void)
{
rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq coming..., irq nest:%d\n",
rt_interrupt_nest));
level = rt_hw_interrupt_disable();
rt_interrupt_nest ++;
rt_hw_interrupt_enable(level);
}
RTM_EXPORT(rt_interrupt_enter);
/**
* This function will be invoked by BSP, when leave interrupt service routine
*
* @note please don't invoke this routine in application
*
* @see rt_interrupt_enter
*/
void rt_interrupt_leave(void)
{
rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq leave, irq nest:%d\n",
rt_interrupt_nest));
level = rt_hw_interrupt_disable();
rt_interrupt_nest --;
rt_hw_interrupt_enable(level);
}
RTM_EXPORT(rt_interrupt_leave);
/**
* This function will return the nest of interrupt.
*
* User application can invoke this function to get whether current
* context is interrupt context.
*
* @return the number of nested interrupts.
*/
rt_uint8_t rt_interrupt_get_nest(void)
{
return rt_interrupt_nest;
}
RTM_EXPORT(rt_interrupt_get_nest);
RTM_EXPORT(rt_hw_interrupt_disable);
RTM_EXPORT(rt_hw_interrupt_enable);
/*@}*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,591 @@
/*
* File : mem.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2008-7-12 Bernard the first version
* 2010-06-09 Bernard fix the end stub of heap
* fix memory check in rt_realloc function
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-14 Bernard fix rt_realloc issue when realloc a NULL pointer.
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
* Simon Goldschmidt
*
*/
#include <rthw.h>
#include <rtthread.h>
#ifndef RT_USING_MEMHEAP_AS_HEAP
/* #define RT_MEM_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;
rt_uint32_t begin_align = RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE);
rt_uint32_t end_align = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_ALIGN_SIZE);
RT_DEBUG_NOT_IN_INTERRUPT;
/* alignment addr */
if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&
((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align))
{
/* calculate the aligned memory size */
mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM;
}
else
{
rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n",
(rt_uint32_t)begin_addr, (rt_uint32_t)end_addr);
return;
}
/* point to begin address of heap */
heap_ptr = (rt_uint8_t *)begin_align;
RT_DEBUG_LOG(RT_DEBUG_MEM, ("mem init, heap begin address 0x%x, size %d\n",
(rt_uint32_t)heap_ptr, mem_size_aligned));
/* initialize the start of the heap */
mem = (struct heap_mem *)heap_ptr;
mem->magic = HEAP_MAGIC;
mem->next = mem_size_aligned + SIZEOF_STRUCT_MEM;
mem->prev = 0;
mem->used = 0;
/* initialize the end of the heap */
heap_end = (struct heap_mem *)&heap_ptr[mem->next];
heap_end->magic = HEAP_MAGIC;
heap_end->used = 1;
heap_end->next = mem_size_aligned + SIZEOF_STRUCT_MEM;
heap_end->prev = mem_size_aligned + SIZEOF_STRUCT_MEM;
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;
RT_DEBUG_NOT_IN_INTERRUPT;
if (size == 0)
return RT_NULL;
if (size != RT_ALIGN(size, RT_ALIGN_SIZE))
RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d\n",
size, RT_ALIGN(size, RT_ALIGN_SIZE)));
else
RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d\n", size));
/* alignment size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
if (size > mem_size_aligned)
{
RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory\n"));
return RT_NULL;
}
/* every data block must be at least MIN_SIZE_ALIGNED long */
if (size < MIN_SIZE_ALIGNED)
size = MIN_SIZE_ALIGNED;
/* take memory semaphore */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
for (ptr = (rt_uint8_t *)lfree - heap_ptr;
ptr < mem_size_aligned - size;
ptr = ((struct heap_mem *)&heap_ptr[ptr])->next)
{
mem = (struct heap_mem *)&heap_ptr[ptr];
if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size)
{
/* mem is not used and at least perfect fit is possible:
* mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >=
(size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED))
{
/* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing
* at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
* -> split large block, create empty remainder,
* remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
* mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
* struct heap_mem would fit in but no data between mem2 and mem2->next
* @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
* region that couldn't hold data, but when mem->next gets freed,
* the 2 regions would be combined, resulting in more free memory
*/
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
/* create mem2 struct */
mem2 = (struct heap_mem *)&heap_ptr[ptr2];
mem2->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 + SIZEOF_STRUCT_MEM)
{
((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2;
}
#ifdef RT_MEM_STATS
used_mem += (size + SIZEOF_STRUCT_MEM);
if (max_mem < used_mem)
max_mem = used_mem;
#endif
}
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
}
/* set memory block magic */
mem->magic = HEAP_MAGIC;
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);
RT_DEBUG_LOG(RT_DEBUG_MEM,
("allocate memory at 0x%x, size: %d\n",
(rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM),
(rt_uint32_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr))));
RT_OBJECT_HOOK_CALL(rt_malloc_hook,
(((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size));
/* return the memory data except mem struct */
return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM;
}
}
rt_sem_release(&heap_sem);
return RT_NULL;
}
RTM_EXPORT(rt_malloc);
/**
* This function will change the previously allocated memory block.
*
* @param rmem pointer to memory allocated by rt_malloc
* @param newsize the required new size
*
* @return the changed memory block address
*/
void *rt_realloc(void *rmem, rt_size_t newsize)
{
rt_size_t size;
rt_size_t ptr, ptr2;
struct heap_mem *mem, *mem2;
void *nmem;
RT_DEBUG_NOT_IN_INTERRUPT;
/* alignment size */
newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
if (newsize > mem_size_aligned)
{
RT_DEBUG_LOG(RT_DEBUG_MEM, ("realloc: out of memory\n"));
return RT_NULL;
}
/* allocate a new memory block */
if (rmem == RT_NULL)
return rt_malloc(newsize);
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr ||
(rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end)
{
/* illegal memory */
rt_sem_release(&heap_sem);
return rmem;
}
mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);
ptr = (rt_uint8_t *)mem - heap_ptr;
size = mem->next - ptr - SIZEOF_STRUCT_MEM;
if (size == newsize)
{
/* the size is the same as */
rt_sem_release(&heap_sem);
return rmem;
}
if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size)
{
/* split memory block */
#ifdef RT_MEM_STATS
used_mem -= (size - newsize);
#endif
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 + SIZEOF_STRUCT_MEM)
{
((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 */
nmem = rt_malloc(newsize);
if (nmem != RT_NULL) /* check memory */
{
rt_memcpy(nmem, rmem, size < newsize ? size : newsize);
rt_free(rmem);
}
return nmem;
}
RTM_EXPORT(rt_realloc);
/**
* This function will contiguously allocate enough space for count objects
* that are size bytes of memory each and returns a pointer to the allocated
* memory.
*
* The allocated memory is filled with bytes of value zero.
*
* @param count number of objects to allocate
* @param size size of the objects to allocate
*
* @return pointer to allocated memory / NULL pointer if there is an error
*/
void *rt_calloc(rt_size_t count, rt_size_t size)
{
void *p;
RT_DEBUG_NOT_IN_INTERRUPT;
/* allocate 'count' objects of size 'size' */
p = rt_malloc(count * size);
/* zero the memory */
if (p)
rt_memset(p, 0, count * size);
return p;
}
RTM_EXPORT(rt_calloc);
/**
* This function will release the previously allocated memory block by
* rt_malloc. The released memory block is taken back to system heap.
*
* @param rmem the address of memory which will be released
*/
void rt_free(void *rmem)
{
struct heap_mem *mem;
RT_DEBUG_NOT_IN_INTERRUPT;
if (rmem == RT_NULL)
return;
RT_ASSERT((((rt_uint32_t)rmem) & (RT_ALIGN_SIZE-1)) == 0);
RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)heap_ptr &&
(rt_uint8_t *)rmem < (rt_uint8_t *)heap_end);
RT_OBJECT_HOOK_CALL(rt_free_hook, (rmem));
if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr ||
(rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end)
{
RT_DEBUG_LOG(RT_DEBUG_MEM, ("illegal memory\n"));
return;
}
/* Get the corresponding struct heap_mem ... */
mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);
RT_DEBUG_LOG(RT_DEBUG_MEM,
("release memory 0x%x, size: %d\n",
(rt_uint32_t)rmem,
(rt_uint32_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr))));
/* protect the heap from concurrent access */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
/* ... which has to be in a used state ... */
RT_ASSERT(mem->used);
RT_ASSERT(mem->magic == HEAP_MAGIC);
/* ... and is now unused. */
mem->used = 0;
mem->magic = 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);
}
RTM_EXPORT(rt_free);
#ifdef RT_MEM_STATS
void rt_memory_info(rt_uint32_t *total,
rt_uint32_t *used,
rt_uint32_t *max_used)
{
if (total != RT_NULL)
*total = mem_size_aligned;
if (used != RT_NULL)
*used = used_mem;
if (max_used != RT_NULL)
*max_used = max_mem;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
void list_mem(void)
{
rt_kprintf("total memory: %d\n", mem_size_aligned);
rt_kprintf("used memory : %d\n", used_mem);
rt_kprintf("maximum allocated memory: %d\n", max_mem);
}
FINSH_FUNCTION_EXPORT(list_mem, list memory usage information)
#endif
#endif
/*@}*/
#endif /* end of RT_USING_HEAP */
#endif /* end of RT_USING_MEMHEAP_AS_HEAP */
@@ -0,0 +1,704 @@
/*
* File : memheap.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2012-04-10 Bernard first implementation
* 2012-10-16 Bernard add the mutex lock for heap object.
* 2012-12-29 Bernard memheap can be used as system heap.
* change mutex lock to semaphore lock.
* 2013-04-10 Bernard add rt_memheap_realloc function.
* 2013-05-24 Bernard fix the rt_memheap_realloc issue.
* 2013-07-11 Grissiom fix the memory block splitting issue.
* 2013-07-15 Grissiom optimize rt_memheap_realloc
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MEMHEAP
/* dynamic pool magic and mask */
#define RT_MEMHEAP_MAGIC 0x1ea01ea0
#define RT_MEMHEAP_MASK 0xfffffffe
#define RT_MEMHEAP_USED 0x01
#define RT_MEMHEAP_FREED 0x00
#define RT_MEMHEAP_IS_USED(i) ((i)->magic & RT_MEMHEAP_USED)
#define RT_MEMHEAP_MINIALLOC 12
#define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE)
#define MEMITEM_SIZE(item) ((rt_uint32_t)item->next - (rt_uint32_t)item - RT_MEMHEAP_SIZE)
/*
* The initialized memory pool will be:
* +-----------------------------------+--------------------------+
* | whole freed memory block | Used Memory Block Tailer |
* +-----------------------------------+--------------------------+
*
* block_list --> whole freed memory block
*
* The length of Used Memory Block Tailer is 0,
* which is prevents block merging across list
*/
rt_err_t rt_memheap_init(struct rt_memheap *memheap,
const char *name,
void *start_addr,
rt_uint32_t size)
{
struct rt_memheap_item *item;
RT_ASSERT(memheap != RT_NULL);
/* initialize pool object */
rt_object_init(&(memheap->parent), RT_Object_Class_MemHeap, name);
memheap->start_addr = start_addr;
memheap->pool_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE);
memheap->max_used_size = memheap->pool_size - memheap->available_size;
/* initialize the free list header */
item = &(memheap->free_header);
item->magic = RT_MEMHEAP_MAGIC;
item->pool_ptr = memheap;
item->next = RT_NULL;
item->prev = RT_NULL;
item->next_free = item;
item->prev_free = item;
/* set the free list to free list header */
memheap->free_list = item;
/* initialize the first big memory block */
item = (struct rt_memheap_item *)start_addr;
item->magic = RT_MEMHEAP_MAGIC;
item->pool_ptr = memheap;
item->next = RT_NULL;
item->prev = RT_NULL;
item->next_free = item;
item->prev_free = item;
item->next = (struct rt_memheap_item *)
((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE);
item->prev = item->next;
/* block list header */
memheap->block_list = item;
/* place the big memory block to free list */
item->next_free = memheap->free_list->next_free;
item->prev_free = memheap->free_list;
memheap->free_list->next_free->prev_free = item;
memheap->free_list->next_free = item;
/* move to the end of memory pool to build a small tailer block,
* which prevents block merging
*/
item = item->next;
/* it's a used memory block */
item->magic = RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED;
item->pool_ptr = memheap;
item->next = (struct rt_memheap_item *)start_addr;
item->prev = (struct rt_memheap_item *)start_addr;
/* not in free list */
item->next_free = item->prev_free = RT_NULL;
/* initialize semaphore lock */
rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_FIFO);
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("memory heap: start addr 0x%08x, size %d, free list header 0x%08x\n",
start_addr, size, &(memheap->free_header)));
return RT_EOK;
}
RTM_EXPORT(rt_memheap_init);
rt_err_t rt_memheap_detach(struct rt_memheap *heap)
{
RT_ASSERT(heap);
rt_object_detach(&(heap->lock.parent.parent));
rt_object_detach(&(heap->parent));
/* Return a successful completion. */
return RT_EOK;
}
RTM_EXPORT(rt_memheap_detach);
void *rt_memheap_alloc(struct rt_memheap *heap, rt_uint32_t size)
{
rt_err_t result;
rt_uint32_t free_size;
struct rt_memheap_item *header_ptr;
RT_ASSERT(heap != RT_NULL);
/* align allocated size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
if (size < RT_MEMHEAP_MINIALLOC)
size = RT_MEMHEAP_MINIALLOC;
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate %d on heap:%8.*s",
size, RT_NAME_MAX, heap->parent.name));
if (size < heap->available_size)
{
/* search on free list */
free_size = 0;
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
/* get the first free memory block */
header_ptr = heap->free_list->next_free;
while (header_ptr != heap->free_list && free_size < size)
{
/* get current freed memory block size */
free_size = MEMITEM_SIZE(header_ptr);
if (free_size < size)
{
/* move to next free memory block */
header_ptr = header_ptr->next_free;
}
}
/* determine if the memory is available. */
if (free_size >= size)
{
/* a block that satisfies the request has been found. */
/* determine if the block needs to be split. */
if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC))
{
struct rt_memheap_item *new_ptr;
/* split the block. */
new_ptr = (struct rt_memheap_item *)
(((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE);
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n",
header_ptr,
header_ptr->next,
header_ptr->prev,
new_ptr));
/* mark the new block as a memory block and freed. */
new_ptr->magic = RT_MEMHEAP_MAGIC;
/* put the pool pointer into the new block. */
new_ptr->pool_ptr = heap;
/* break down the block list */
new_ptr->prev = header_ptr;
new_ptr->next = header_ptr->next;
header_ptr->next->prev = new_ptr;
header_ptr->next = new_ptr;
/* remove header ptr from free list */
header_ptr->next_free->prev_free = header_ptr->prev_free;
header_ptr->prev_free->next_free = header_ptr->next_free;
header_ptr->next_free = RT_NULL;
header_ptr->prev_free = RT_NULL;
/* insert new_ptr to free list */
new_ptr->next_free = heap->free_list->next_free;
new_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = new_ptr;
heap->free_list->next_free = new_ptr;
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x\n",
new_ptr->next_free,
new_ptr->prev_free));
/* decrement the available byte count. */
heap->available_size = heap->available_size -
size -
RT_MEMHEAP_SIZE;
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
}
else
{
/* decrement the entire free size from the available bytes count. */
heap->available_size = heap->available_size - free_size;
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
/* remove header_ptr from free list */
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("one block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x\n",
header_ptr,
header_ptr->next_free,
header_ptr->prev_free));
header_ptr->next_free->prev_free = header_ptr->prev_free;
header_ptr->prev_free->next_free = header_ptr->next_free;
header_ptr->next_free = RT_NULL;
header_ptr->prev_free = RT_NULL;
}
/* Mark the allocated block as not available. */
header_ptr->magic |= RT_MEMHEAP_USED;
/* release lock */
rt_sem_release(&(heap->lock));
/* Return a memory address to the caller. */
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("alloc mem: memory[0x%08x], heap[0x%08x], size: %d\n",
(void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE),
header_ptr,
size));
return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE);
}
/* release lock */
rt_sem_release(&(heap->lock));
}
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n"));
/* Return the completion status. */
return RT_NULL;
}
RTM_EXPORT(rt_memheap_alloc);
void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize)
{
rt_err_t result;
rt_size_t oldsize;
struct rt_memheap_item *header_ptr;
struct rt_memheap_item *new_ptr;
if (newsize == 0)
{
rt_memheap_free(ptr);
return RT_NULL;
}
/* align allocated size */
newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
if (newsize < RT_MEMHEAP_MINIALLOC)
newsize = RT_MEMHEAP_MINIALLOC;
if (ptr == RT_NULL)
{
return rt_memheap_alloc(heap, newsize);
}
/* get memory block header and get the size of memory block */
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
oldsize = MEMITEM_SIZE(header_ptr);
/* re-allocate memory */
if (newsize > oldsize)
{
void* new_ptr;
struct rt_memheap_item *next_ptr;
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
next_ptr = header_ptr->next;
/* header_ptr should not be the tail */
RT_ASSERT(next_ptr > header_ptr);
/* check whether the following free space is enough to expand */
if (!RT_MEMHEAP_IS_USED(next_ptr))
{
rt_int32_t nextsize;
nextsize = MEMITEM_SIZE(next_ptr);
RT_ASSERT(next_ptr > 0);
/* Here is the ASCII art of the situation that we can make use of
* the next free node without alloc/memcpy, |*| is the control
* block:
*
* oldsize free node
* |*|-----------|*|----------------------|*|
* newsize >= minialloc
* |*|----------------|*|-----------------|*|
*/
if (nextsize + oldsize > newsize + RT_MEMHEAP_MINIALLOC)
{
/* decrement the entire free size from the available bytes count. */
heap->available_size = heap->available_size - (newsize - oldsize);
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
/* remove next_ptr from free list */
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("remove block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x",
next_ptr,
next_ptr->next_free,
next_ptr->prev_free));
next_ptr->next_free->prev_free = next_ptr->prev_free;
next_ptr->prev_free->next_free = next_ptr->next_free;
next_ptr->next->prev = next_ptr->prev;
next_ptr->prev->next = next_ptr->next;
/* build a new one on the right place */
next_ptr = (struct rt_memheap_item*)((char*)ptr + newsize);
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("new free block: block[0x%08x] nextm[0x%08x] prevm[0x%08x]",
next_ptr,
next_ptr->next,
next_ptr->prev));
/* mark the new block as a memory block and freed. */
next_ptr->magic = RT_MEMHEAP_MAGIC;
/* put the pool pointer into the new block. */
next_ptr->pool_ptr = heap;
next_ptr->prev = header_ptr;
next_ptr->next = header_ptr->next;
header_ptr->next->prev = next_ptr;
header_ptr->next = next_ptr;
/* insert next_ptr to free list */
next_ptr->next_free = heap->free_list->next_free;
next_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = next_ptr;
heap->free_list->next_free = next_ptr;
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x",
next_ptr->next_free,
next_ptr->prev_free));
/* release lock */
rt_sem_release(&(heap->lock));
return ptr;
}
}
/* release lock */
rt_sem_release(&(heap->lock));
/* re-allocate a memory block */
new_ptr = (void*)rt_memheap_alloc(heap, newsize);
if (new_ptr != RT_NULL)
{
rt_memcpy(new_ptr, ptr, oldsize < newsize ? oldsize : newsize);
rt_memheap_free(ptr);
}
return new_ptr;
}
/* don't split when there is less than one node space left */
if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize)
return ptr;
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
/* split the block. */
new_ptr = (struct rt_memheap_item *)
(((rt_uint8_t *)header_ptr) + newsize + RT_MEMHEAP_SIZE);
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n",
header_ptr,
header_ptr->next,
header_ptr->prev,
new_ptr));
/* mark the new block as a memory block and freed. */
new_ptr->magic = RT_MEMHEAP_MAGIC;
/* put the pool pointer into the new block. */
new_ptr->pool_ptr = heap;
/* break down the block list */
new_ptr->prev = header_ptr;
new_ptr->next = header_ptr->next;
header_ptr->next->prev = new_ptr;
header_ptr->next = new_ptr;
/* determine if the block can be merged with the next neighbor. */
if (!RT_MEMHEAP_IS_USED(new_ptr->next))
{
struct rt_memheap_item *free_ptr;
/* merge block with next neighbor. */
free_ptr = new_ptr->next;
heap->available_size = heap->available_size - MEMITEM_SIZE(free_ptr);
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n",
header_ptr, header_ptr->next_free, header_ptr->prev_free));
free_ptr->next->prev = new_ptr;
new_ptr->next = free_ptr->next;
/* remove free ptr from free list */
free_ptr->next_free->prev_free = free_ptr->prev_free;
free_ptr->prev_free->next_free = free_ptr->next_free;
}
/* insert the split block to free list */
new_ptr->next_free = heap->free_list->next_free;
new_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = new_ptr;
heap->free_list->next_free = new_ptr;
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new free ptr: next_free 0x%08x, prev_free 0x%08x\n",
new_ptr->next_free,
new_ptr->prev_free));
/* increment the available byte count. */
heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr);
/* release lock */
rt_sem_release(&(heap->lock));
/* return the old memory block */
return ptr;
}
RTM_EXPORT(rt_memheap_realloc);
void rt_memheap_free(void *ptr)
{
rt_err_t result;
struct rt_memheap *heap;
struct rt_memheap_item *header_ptr, *new_ptr;
rt_uint32_t insert_header;
/* NULL check */
if (ptr == RT_NULL) return;
/* set initial status as OK */
insert_header = 1;
new_ptr = RT_NULL;
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: memory[0x%08x], block[0x%08x]\n",
ptr, header_ptr));
/* check magic */
RT_ASSERT((header_ptr->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);
/* get pool ptr */
heap = header_ptr->pool_ptr;
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return ;
}
/* Mark the memory as available. */
header_ptr->magic &= ~RT_MEMHEAP_USED;
/* Adjust the available number of bytes. */
heap->available_size = heap->available_size + MEMITEM_SIZE(header_ptr);
/* Determine if the block can be merged with the previous neighbor. */
if (!RT_MEMHEAP_IS_USED(header_ptr->prev))
{
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("merge: left node 0x%08x\n",
header_ptr->prev));
/* adjust the available number of bytes. */
heap->available_size = heap->available_size + RT_MEMHEAP_SIZE;
/* yes, merge block with previous neighbor. */
(header_ptr->prev)->next = header_ptr->next;
(header_ptr->next)->prev = header_ptr->prev;
/* move header pointer to previous. */
header_ptr = header_ptr->prev;
/* don't insert header to free list */
insert_header = 0;
}
/* determine if the block can be merged with the next neighbor. */
if (!RT_MEMHEAP_IS_USED(header_ptr->next))
{
/* adjust the available number of bytes. */
heap->available_size = heap->available_size + RT_MEMHEAP_SIZE;
/* merge block with next neighbor. */
new_ptr = header_ptr->next;
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n",
new_ptr, new_ptr->next_free, new_ptr->prev_free));
new_ptr->next->prev = header_ptr;
header_ptr->next = new_ptr->next;
/* remove new ptr from free list */
new_ptr->next_free->prev_free = new_ptr->prev_free;
new_ptr->prev_free->next_free = new_ptr->next_free;
}
if (insert_header)
{
/* no left merge, insert to free list */
header_ptr->next_free = heap->free_list->next_free;
header_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = header_ptr;
heap->free_list->next_free = header_ptr;
RT_DEBUG_LOG(RT_DEBUG_MEMHEAP,
("insert to free list: next_free 0x%08x, prev_free 0x%08x\n",
header_ptr->next_free, header_ptr->prev_free));
}
/* release lock */
rt_sem_release(&(heap->lock));
}
RTM_EXPORT(rt_memheap_free);
#ifdef RT_USING_MEMHEAP_AS_HEAP
static struct rt_memheap _heap;
void rt_system_heap_init(void *begin_addr, void *end_addr)
{
/* initialize a default heap in the system */
rt_memheap_init(&_heap,
"heap",
begin_addr,
(rt_uint32_t)end_addr - (rt_uint32_t)begin_addr);
}
void *rt_malloc(rt_size_t size)
{
void* ptr;
/* try to allocate in system heap */
ptr = rt_memheap_alloc(&_heap, size);
if (ptr == RT_NULL)
{
struct rt_object *object;
struct rt_list_node *node;
struct rt_memheap *heap;
struct rt_object_information *information;
extern struct rt_object_information rt_object_container[];
/* try to allocate on other memory heap */
information = &rt_object_container[RT_Object_Class_MemHeap];
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
heap = (struct rt_memheap *)object;
/* not allocate in the default system heap */
if (heap == &_heap)
continue;
ptr = rt_memheap_alloc(heap, size);
if (ptr != RT_NULL)
break;
}
}
return ptr;
}
RTM_EXPORT(rt_malloc);
void rt_free(void *rmem)
{
rt_memheap_free(rmem);
}
RTM_EXPORT(rt_free);
void *rt_realloc(void *rmem, rt_size_t newsize)
{
void *new_ptr;
struct rt_memheap_item *header_ptr;
if (rmem == RT_NULL)
return rt_malloc(newsize);
/* get old memory item */
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)rmem - RT_MEMHEAP_SIZE);
new_ptr = rt_memheap_realloc(header_ptr->pool_ptr, rmem, newsize);
if (new_ptr == RT_NULL && newsize != 0)
{
/* allocate memory block from other memheap */
new_ptr = rt_malloc(newsize);
if (new_ptr != RT_NULL && rmem != RT_NULL)
{
rt_size_t oldsize;
/* get the size of old memory block */
oldsize = MEMITEM_SIZE(header_ptr);
if (newsize > oldsize)
rt_memcpy(new_ptr, rmem, oldsize);
else
rt_memcpy(new_ptr, rmem, newsize);
}
}
return new_ptr;
}
RTM_EXPORT(rt_realloc);
void *rt_calloc(rt_size_t count, rt_size_t size)
{
void *ptr;
rt_size_t total_size;
total_size = count * size;
ptr = rt_malloc(total_size);
if (ptr != RT_NULL)
{
/* clean memory */
rt_memset(ptr, 0, total_size);
}
return ptr;
}
RTM_EXPORT(rt_calloc);
#endif
#endif
@@ -0,0 +1,470 @@
/*
* File : mempool.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-05-27 Bernard implement memory pool
* 2006-06-03 Bernard fix the thread timer init bug
* 2006-06-30 Bernard fix the allocate/free block bug
* 2006-08-04 Bernard add hook support
* 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-26 yi.qiu add module support in rt_mp_delete
* 2011-01-24 Bernard add object allocation check.
* 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create.
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MEMPOOL
#ifdef RT_USING_HOOK
static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block);
static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block);
/**
* @addtogroup Hook
*/
/*@{*/
/**
* This function will set a hook function, which will be invoked when a memory
* block is allocated from memory pool.
*
* @param hook the hook function
*/
void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block))
{
rt_mp_alloc_hook = hook;
}
/**
* This function will set a hook function, which will be invoked when a memory
* block is released to memory pool.
*
* @param hook the hook function
*/
void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block))
{
rt_mp_free_hook = hook;
}
/*@}*/
#endif
/**
* @addtogroup MM
*/
/*@{*/
/**
* This function will initialize a memory pool object, normally which is used
* for static object.
*
* @param mp the memory pool object
* @param name the name of memory pool
* @param start the star address of memory pool
* @param size the total size of memory pool
* @param block_size the size for each block
*
* @return RT_EOK
*/
rt_err_t rt_mp_init(struct rt_mempool *mp,
const char *name,
void *start,
rt_size_t size,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
register rt_base_t offset;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
/* initialize object */
rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
/* initialize memory pool */
mp->start_address = start;
mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
/* align the block size */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
/* align to align size byte */
mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
mp->suspend_thread_count = 0;
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
(rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
RT_NULL;
mp->block_list = block_ptr;
return RT_EOK;
}
RTM_EXPORT(rt_mp_init);
/**
* This function will detach a memory pool from system object management.
*
* @param mp the memory pool object
*
* @return RT_EOK
*/
rt_err_t rt_mp_detach(struct rt_mempool *mp)
{
struct rt_thread *thread;
register rt_ubase_t temp;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
/* wake up 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;
}
RTM_EXPORT(rt_mp_detach);
#ifdef RT_USING_HEAP
/**
* This function will create a mempool object and allocate the memory pool from
* heap.
*
* @param name the name of memory pool
* @param block_count the count of blocks in memory pool
* @param block_size the size for each block
*
* @return the created mempool object
*/
rt_mp_t rt_mp_create(const char *name,
rt_size_t block_count,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
struct rt_mempool *mp;
register rt_base_t offset;
RT_DEBUG_NOT_IN_INTERRUPT;
/* allocate object */
mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
/* allocate object failed */
if (mp == RT_NULL)
return RT_NULL;
/* initialize memory pool */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count;
/* allocate memory */
mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
block_count);
if (mp->start_address == RT_NULL)
{
/* no memory, delete memory pool object */
rt_object_delete(&(mp->parent));
return RT_NULL;
}
mp->block_total_count = block_count;
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
mp->suspend_thread_count = 0;
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
= block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
= RT_NULL;
mp->block_list = block_ptr;
return mp;
}
RTM_EXPORT(rt_mp_create);
/**
* This function will delete a memory pool and release the object memory.
*
* @param mp the memory pool object
*
* @return RT_EOK
*/
rt_err_t rt_mp_delete(rt_mp_t mp)
{
struct rt_thread *thread;
register rt_ubase_t temp;
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
/* wake up 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);
}
#if defined(RT_USING_MODULE) && defined(RT_USING_SLAB)
/* the mp object belongs to an application module */
if (mp->parent.flag & RT_OBJECT_FLAG_MODULE)
rt_module_free(mp->parent.module_id, mp->start_address);
else
#endif
/* release allocated room */
rt_free(mp->start_address);
/* detach object */
rt_object_delete(&(mp->parent));
return RT_EOK;
}
RTM_EXPORT(rt_mp_delete);
#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 or RT_NULL on allocated failed
*/
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
{
rt_uint8_t *block_ptr;
register rt_base_t level;
struct rt_thread *thread;
/* 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)
{
/* enable interrupt */
rt_hw_interrupt_enable(level);
return RT_NULL;
}
else
{
RT_DEBUG_NOT_IN_INTERRUPT;
/* get current thread */
thread = rt_thread_self();
thread->error = RT_EOK;
/* 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);
RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
(mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));
return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
}
RTM_EXPORT(rt_mp_alloc);
/**
* This function will release a memory block
*
* @param block the address of memory block to be released
*/
void rt_mp_free(void *block)
{
rt_uint8_t **block_ptr;
struct rt_mempool *mp;
struct rt_thread *thread;
register rt_base_t level;
/* get the control block of pool which the block belongs to */
block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
mp = (struct rt_mempool *)*block_ptr;
RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* increase the free block count */
mp->block_free_count ++;
/* link the block into the block list */
*block_ptr = mp->block_list;
mp->block_list = (rt_uint8_t *)block_ptr;
if (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);
}
RTM_EXPORT(rt_mp_free);
/*@}*/
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,258 @@
/*
* File : module.h
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2010-01-09 Bernard first version
* 2010-04-09 yi.qiu implement based on first version
*/
#ifndef __MODULE_H__
#define __MODULE_H__
#include <rtdef.h>
typedef rt_uint8_t Elf_Byte;
typedef rt_uint32_t Elf32_Addr; /* Unsigned program address */
typedef rt_uint32_t Elf32_Off; /* Unsigned file offset */
typedef rt_int32_t Elf32_Sword; /* Signed large integer */
typedef rt_uint32_t Elf32_Word; /* Unsigned large integer */
typedef rt_uint16_t Elf32_Half; /* Unsigned medium integer */
/* e_ident[] magic number */
#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */
#define ELFMAG1 'E' /* e_ident[EI_MAG1] */
#define ELFMAG2 'L' /* e_ident[EI_MAG2] */
#define ELFMAG3 'F' /* e_ident[EI_MAG3] */
#define RTMMAG "\177RTM" /* magic */
#define ELFMAG "\177ELF" /* magic */
#define SELFMAG 4 /* size of magic */
#define EI_CLASS 4 /* file class */
#define EI_NIDENT 16 /* Size of e_ident[] */
/* e_ident[] file class */
#define ELFCLASSNONE 0 /* invalid */
#define ELFCLASS32 1 /* 32-bit objs */
#define ELFCLASS64 2 /* 64-bit objs */
#define ELFCLASSNUM 3 /* number of classes */
/* e_ident[] data encoding */
#define ELFDATANONE 0 /* invalid */
#define ELFDATA2LSB 1 /* Little-Endian */
#define ELFDATA2MSB 2 /* Big-Endian */
#define ELFDATANUM 3 /* number of data encode defines */
/* e_ident */
#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
(ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
(ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
(ehdr).e_ident[EI_MAG3] == ELFMAG3)
#define ET_NONE 0 /* No file type */
#define ET_REL 1 /* Relocatable file */
#define ET_EXEC 2 /* Executable file */
#define ET_DYN 3 /* Shared object file */
#define ET_CORE 4 /* Core file */
/* ELF Header */
typedef struct elfhdr
{
unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
Elf32_Half e_type; /* object file type */
Elf32_Half e_machine; /* machine */
Elf32_Word e_version; /* object file version */
Elf32_Addr e_entry; /* virtual entry point */
Elf32_Off e_phoff; /* program header table offset */
Elf32_Off e_shoff; /* section header table offset */
Elf32_Word e_flags; /* processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size */
Elf32_Half e_phentsize; /* program header entry size */
Elf32_Half e_phnum; /* number of program header entries */
Elf32_Half e_shentsize; /* section header entry size */
Elf32_Half e_shnum; /* number of section header entries */
Elf32_Half e_shstrndx; /* section header table's "section
header string table" entry offset */
} Elf32_Ehdr;
/* Section Header */
typedef struct
{
Elf32_Word sh_name; /* name - index into section header
string table section */
Elf32_Word sh_type; /* type */
Elf32_Word sh_flags; /* flags */
Elf32_Addr sh_addr; /* address */
Elf32_Off sh_offset; /* file offset */
Elf32_Word sh_size; /* section size */
Elf32_Word sh_link; /* section header table index link */
Elf32_Word sh_info; /* extra information */
Elf32_Word sh_addralign; /* address alignment */
Elf32_Word sh_entsize; /* section entry size */
} Elf32_Shdr;
/* Section names */
#define ELF_BSS ".bss" /* uninitialized data */
#define ELF_DATA ".data" /* initialized data */
#define ELF_DEBUG ".debug" /* debug */
#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */
#define ELF_DYNSTR ".dynstr" /* dynamic string table */
#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */
#define ELF_FINI ".fini" /* termination code */
#define ELF_GOT ".got" /* global offset table */
#define ELF_HASH ".hash" /* symbol hash table */
#define ELF_INIT ".init" /* initialization code */
#define ELF_REL_DATA ".rel.data" /* relocation data */
#define ELF_REL_FINI ".rel.fini" /* relocation termination code */
#define ELF_REL_INIT ".rel.init" /* relocation initialization code */
#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */
#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */
#define ELF_REL_TEXT ".rel.text" /* relocation code */
#define ELF_RODATA ".rodata" /* read-only data */
#define ELF_SHSTRTAB ".shstrtab" /* section header string table */
#define ELF_STRTAB ".strtab" /* string table */
#define ELF_SYMTAB ".symtab" /* symbol table */
#define ELF_TEXT ".text" /* code */
#define ELF_RTMSYMTAB "RTMSymTab"
/* Symbol Table Entry */
typedef struct elf32_sym
{
Elf32_Word st_name; /* name - index into string table */
Elf32_Addr st_value; /* symbol value */
Elf32_Word st_size; /* symbol size */
unsigned char st_info; /* type and binding */
unsigned char st_other; /* 0 - no defined meaning */
Elf32_Half st_shndx; /* section header index */
} Elf32_Sym;
#define STB_LOCAL 0 /* BIND */
#define STB_GLOBAL 1
#define STB_WEAK 2
#define STB_NUM 3
#define STB_LOPROC 13 /* processor specific range */
#define STB_HIPROC 15
#define STT_NOTYPE 0 /* symbol type is unspecified */
#define STT_OBJECT 1 /* data object */
#define STT_FUNC 2 /* code object */
#define STT_SECTION 3 /* symbol identifies an ELF section */
#define STT_FILE 4 /* symbol's name is file name */
#define STT_COMMON 5 /* common data object */
#define STT_TLS 6 /* thread-local data object */
#define STT_NUM 7 /* # defined types in generic range */
#define STT_LOOS 10 /* OS specific range */
#define STT_HIOS 12
#define STT_LOPROC 13 /* processor specific range */
#define STT_HIPROC 15
#define STN_UNDEF 0 /* undefined */
#define ELF_ST_BIND(info) ((info) >> 4)
#define ELF_ST_TYPE(info) ((info) & 0xf)
#define ELF_ST_INFO(bind, type) (((bind)<<4)+((type)&0xf))
/* Relocation entry with implicit addend */
typedef struct
{
Elf32_Addr r_offset; /* offset of relocation */
Elf32_Word r_info; /* symbol table index and type */
} Elf32_Rel;
/* Relocation entry with explicit addend */
typedef struct
{
Elf32_Addr r_offset; /* offset of relocation */
Elf32_Word r_info; /* symbol table index and type */
Elf32_Sword r_addend;
} Elf32_Rela;
/* Extract relocation info - r_info */
#define ELF32_R_SYM(i) ((i) >> 8)
#define ELF32_R_TYPE(i) ((unsigned char) (i))
#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t))
/*
* Relocation type for arm
*/
#define R_ARM_NONE 0
#define R_ARM_PC24 1
#define R_ARM_ABS32 2
#define R_ARM_REL32 3
#define R_ARM_THM_CALL 10
#define R_ARM_GLOB_DAT 21
#define R_ARM_JUMP_SLOT 22
#define R_ARM_RELATIVE 23
#define R_ARM_GOT_BREL 26
#define R_ARM_PLT32 27
#define R_ARM_CALL 28
#define R_ARM_JUMP24 29
#define R_ARM_THM_JUMP24 30
#define R_ARM_V4BX 40
/* Program Header */
typedef struct
{
Elf32_Word p_type; /* segment type */
Elf32_Off p_offset; /* segment offset */
Elf32_Addr p_vaddr; /* virtual address of segment */
Elf32_Addr p_paddr; /* physical address - ignored? */
Elf32_Word p_filesz; /* number of bytes in file for seg. */
Elf32_Word p_memsz; /* number of bytes in mem. for seg. */
Elf32_Word p_flags; /* flags */
Elf32_Word p_align; /* memory alignment */
} Elf32_Phdr;
/* p_type */
#define PT_LOAD 1
/* p_flags */
#define PF_X 1
#define PF_W 2
#define PF_R 4
/* sh_type */
#define SHT_NULL 0 /* inactive */
#define SHT_PROGBITS 1 /* program defined information */
#define SHT_SYMTAB 2 /* symbol table section */
#define SHT_STRTAB 3 /* string table section */
#define SHT_RELA 4 /* relocation section with addends*/
#define SHT_HASH 5 /* symbol hash table section */
#define SHT_DYNAMIC 6 /* dynamic section */
#define SHT_NOTE 7 /* note section */
#define SHT_NOBITS 8 /* no space section */
#define SHT_REL 9 /* relation section without addends */
#define SHT_SHLIB 10 /* reserved - purpose unknown */
#define SHT_DYNSYM 11 /* dynamic symbol table section */
#define SHT_NUM 12 /* number of section types */
#define SHT_LOPROC 0x70000000 /* reserved range for processor */
#define SHT_HIPROC 0x7fffffff /* specific section header types */
#define SHT_LOUSER 0x80000000 /* reserved range for application */
#define SHT_HIUSER 0xffffffff /* specific indexes */
/* Section Attribute Flags - sh_flags */
#define SHF_WRITE 0x1 /* Writable */
#define SHF_ALLOC 0x2 /* occupies memory */
#define SHF_EXECINSTR 0x4 /* executable */
#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */
/* specific section attributes */
#endif
@@ -0,0 +1,442 @@
/*
* File : object.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-03-14 Bernard the first version
* 2006-04-21 Bernard change the scheduler lock to interrupt lock
* 2006-05-18 Bernard fix the object init bug
* 2006-08-03 Bernard add hook support
* 2007-01-28 Bernard rename RT_OBJECT_Class_Static to RT_Object_Class_Static
* 2010-10-26 yi.qiu add module support in rt_object_allocate and rt_object_free
*/
#include <rtthread.h>
#include <rthw.h>
#define _OBJ_CONTAINER_LIST_INIT(c) \
{&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}
struct rt_object_information rt_object_container[RT_Object_Class_Unknown] =
{
/* initialize object container - thread */
{RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Thread), sizeof(struct rt_thread)},
#ifdef RT_USING_SEMAPHORE
/* initialize object container - semaphore */
{RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Semaphore), sizeof(struct rt_semaphore)},
#endif
#ifdef RT_USING_MUTEX
/* initialize object container - mutex */
{RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Mutex), sizeof(struct rt_mutex)},
#endif
#ifdef RT_USING_EVENT
/* initialize object container - event */
{RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Event), sizeof(struct rt_event)},
#endif
#ifdef RT_USING_MAILBOX
/* initialize object container - mailbox */
{RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_MailBox), sizeof(struct rt_mailbox)},
#endif
#ifdef RT_USING_MESSAGEQUEUE
/* initialize object container - message queue */
{RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_MessageQueue), sizeof(struct rt_messagequeue)},
#endif
#ifdef RT_USING_MEMHEAP
/* initialize object container - memory heap */
{RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_MemHeap), sizeof(struct rt_memheap)},
#endif
#ifdef RT_USING_MEMPOOL
/* initialize object container - memory pool */
{RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_MemPool), sizeof(struct rt_mempool)},
#endif
#ifdef RT_USING_DEVICE
/* initialize object container - device */
{RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Device), sizeof(struct rt_device)},
#endif
/* initialize object container - timer */
{RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Timer), sizeof(struct rt_timer)},
#ifdef RT_USING_MODULE
/* initialize object container - module */
{RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Class_Module), sizeof(struct rt_module)},
#endif
};
#ifdef RT_USING_HOOK
static void (*rt_object_attach_hook)(struct rt_object *object);
static void (*rt_object_detach_hook)(struct rt_object *object);
void (*rt_object_trytake_hook)(struct rt_object *object);
void (*rt_object_take_hook)(struct rt_object *object);
void (*rt_object_put_hook)(struct rt_object *object);
/**
* @addtogroup Hook
*/
/*@{*/
/**
* This function will set a hook function, which will be invoked when object
* attaches to kernel object system.
*
* @param hook the hook function
*/
void rt_object_attach_sethook(void (*hook)(struct rt_object *object))
{
rt_object_attach_hook = hook;
}
/**
* This function will set a hook function, which will be invoked when object
* detaches from kernel object system.
*
* @param hook the hook function
*/
void rt_object_detach_sethook(void (*hook)(struct rt_object *object))
{
rt_object_detach_hook = hook;
}
/**
* This function will set a hook function, which will be invoked when object
* is taken from kernel object system.
*
* The object is taken means:
* semaphore - semaphore is taken by thread
* mutex - mutex is taken by thread
* event - event is received by thread
* mailbox - mail is received by thread
* message queue - message is received by thread
*
* @param hook the hook function
*/
void rt_object_trytake_sethook(void (*hook)(struct rt_object *object))
{
rt_object_trytake_hook = hook;
}
/**
* This function will set a hook function, which will be invoked when object
* have been taken from kernel object system.
*
* The object have been taken means:
* semaphore - semaphore have been taken by thread
* mutex - mutex have been taken by thread
* event - event have been received by thread
* mailbox - mail have been received by thread
* message queue - message have been received by thread
* timer - timer is started
*
* @param hook the hook function
*/
void rt_object_take_sethook(void (*hook)(struct rt_object *object))
{
rt_object_take_hook = hook;
}
/**
* This function will set a hook function, which will be invoked when object
* is put to kernel object system.
*
* @param hook the hook function
*/
void rt_object_put_sethook(void (*hook)(struct rt_object *object))
{
rt_object_put_hook = hook;
}
/*@}*/
#endif
/**
* @ingroup SystemInit
*
* This function will initialize system object management.
*
* @deprecated since 0.3.0, this function does not need to be invoked
* in the system initialization.
*/
void rt_system_object_init(void)
{
}
/**
* @addtogroup KernelObject
*/
/*@{*/
/**
* This function will return the specified type of object information.
*
* @param type the type of object
* @return the object type information or RT_NULL
*/
struct rt_object_information *
rt_object_get_information(enum rt_object_class_type type)
{
return &rt_object_container[type];
}
RTM_EXPORT(rt_object_get_information);
/**
* This function will initialize an object and add it to object system
* management.
*
* @param object the specified object to be initialized.
* @param type the object type.
* @param name the object name. In system, the object's name must be unique.
*/
void rt_object_init(struct rt_object *object,
enum rt_object_class_type type,
const char *name)
{
register rt_base_t temp;
struct rt_object_information *information;
#ifdef RT_USING_MODULE
/* get module object information */
information = (rt_module_self() != RT_NULL) ?
&rt_module_self()->module_object[type] : &rt_object_container[type];
#else
/* get object information */
information = &rt_object_container[type];
#endif
/* initialize object's parameters */
/* set object type to static */
object->type = type | RT_Object_Class_Static;
/* copy name */
rt_strncpy(object->name, name, RT_NAME_MAX);
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
/* lock interrupt */
temp = rt_hw_interrupt_disable();
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
/* unlock interrupt */
rt_hw_interrupt_enable(temp);
}
/**
* This function will detach a static object from object system,
* and the memory of static object is not freed.
*
* @param object the specified object to be detached.
*/
void rt_object_detach(rt_object_t object)
{
register rt_base_t temp;
/* object check */
RT_ASSERT(object != RT_NULL);
RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));
/* 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;
RT_DEBUG_NOT_IN_INTERRUPT;
#ifdef RT_USING_MODULE
/*
* get module object information,
* module object should be managed by kernel object container
*/
information = (rt_module_self() != RT_NULL && (type != RT_Object_Class_Module)) ?
&rt_module_self()->module_object[type] : &rt_object_container[type];
#else
/* get object information */
information = &rt_object_container[type];
#endif
object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size);
if (object == RT_NULL)
{
/* no memory can be allocated */
return RT_NULL;
}
/* initialize object's parameters */
/* set object type */
object->type = type;
/* set object flag */
object->flag = 0;
#ifdef RT_USING_MODULE
if (rt_module_self() != RT_NULL)
{
object->flag |= RT_OBJECT_FLAG_MODULE;
}
object->module_id = (void *)rt_module_self();
#endif
/* copy name */
rt_strncpy(object->name, name, RT_NAME_MAX);
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
/* lock interrupt */
temp = rt_hw_interrupt_disable();
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
/* unlock interrupt */
rt_hw_interrupt_enable(temp);
/* return object */
return object;
}
/**
* This function will delete an object and release object memory.
*
* @param object the specified object to be deleted.
*/
void rt_object_delete(rt_object_t object)
{
register rt_base_t temp;
/* object check */
RT_ASSERT(object != RT_NULL);
RT_ASSERT(!(object->type & RT_Object_Class_Static));
RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));
/* lock interrupt */
temp = rt_hw_interrupt_disable();
/* remove from old list */
rt_list_remove(&(object->list));
/* unlock interrupt */
rt_hw_interrupt_enable(temp);
#if defined(RT_USING_MODULE) && defined(RT_USING_SLAB)
if (object->flag & RT_OBJECT_FLAG_MODULE)
rt_module_free((rt_module_t)object->module_id, object);
else
#endif
/* free the memory of object */
RT_KERNEL_FREE(object);
}
#endif
/**
* This function will judge the object is system object or not.
* Normally, the system object is a static object and the type
* of object set to RT_Object_Class_Static.
*
* @param object the specified object to be judged.
*
* @return RT_TRUE if a system object, RT_FALSE for others.
*/
rt_bool_t rt_object_is_systemobject(rt_object_t object)
{
/* object check */
RT_ASSERT(object != RT_NULL);
if (object->type & RT_Object_Class_Static)
return RT_TRUE;
return RT_FALSE;
}
/**
* This function will find specified name object from object
* container.
*
* @param name the specified name of object.
* @param type the type of object
*
* @return the found object or RT_NULL if there is no this object
* in object container.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_object_t rt_object_find(const char *name, rt_uint8_t type)
{
struct rt_object *object;
struct rt_list_node *node;
struct rt_object_information *information;
extern volatile rt_uint8_t rt_interrupt_nest;
/* parameter check */
if ((name == RT_NULL) || (type > RT_Object_Class_Unknown))
return RT_NULL;
/* which is invoke in interrupt status */
if (rt_interrupt_nest != 0)
RT_ASSERT(0);
/* enter critical */
rt_enter_critical();
/* try to find object */
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)
{
/* leave critical */
rt_exit_critical();
return object;
}
}
/* leave critical */
rt_exit_critical();
return RT_NULL;
}
/*@}*/
@@ -0,0 +1,412 @@
/*
* File : scheduler.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-03-17 Bernard the first version
* 2006-04-28 Bernard fix the scheduler algorthm
* 2006-04-30 Bernard add SCHEDULER_DEBUG
* 2006-05-27 Bernard fix the scheduler algorthm for same priority
* thread schedule
* 2006-06-04 Bernard rewrite the scheduler algorithm
* 2006-08-03 Bernard add hook support
* 2006-09-05 Bernard add 32 priority level support
* 2006-09-24 Bernard add rt_system_scheduler_start function
* 2009-09-16 Bernard fix _rt_scheduler_stack_check
* 2010-04-11 yi.qiu add module feature
* 2010-07-13 Bernard fix the maximal number of rt_scheduler_lock_nest
* issue found by kuronca
* 2010-12-13 Bernard add defunct list initialization even if not use heap.
* 2011-05-10 Bernard clean scheduler debug log.
* 2013-12-21 Grissiom add rt_critical_level
*/
#include <rtthread.h>
#include <rthw.h>
static rt_int16_t rt_scheduler_lock_nest;
extern volatile rt_uint8_t rt_interrupt_nest;
extern int __rt_ffs(int value);
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
/* Maximum priority level, 256 */
rt_uint32_t rt_thread_ready_priority_group;
rt_uint8_t rt_thread_ready_table[32];
#else
/* Maximum priority level, 32 */
rt_uint32_t rt_thread_ready_priority_group;
#endif
rt_list_t rt_thread_defunct;
#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)thread->sp >
(rt_uint32_t)thread->stack_addr + (rt_uint32_t)thread->stack_size)
{
rt_uint32_t level;
rt_kprintf("thread:%s stack overflow\n", thread->name);
#ifdef RT_USING_FINSH
{
extern long list_thread(void);
list_thread();
}
#endif
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 initialize the system scheduler
*/
void rt_system_scheduler_init(void)
{
register rt_base_t offset;
rt_scheduler_lock_nest = 0;
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("start scheduler: max priority 0x%02x\n",
RT_THREAD_PRIORITY_MAX));
for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
{
rt_list_init(&rt_thread_priority_table[offset]);
}
rt_current_priority = RT_THREAD_PRIORITY_MAX - 1;
rt_current_thread = RT_NULL;
/* initialize ready priority group */
rt_thread_ready_priority_group = 0;
#if RT_THREAD_PRIORITY_MAX > 32
/* initialize ready table */
rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
#endif
/* initialize thread defunct */
rt_list_init(&rt_thread_defunct);
}
/**
* @ingroup SystemInit
* This function will startup scheduler. It will select one thread
* with the highest priority level, then switch to it.
*/
void rt_system_scheduler_start(void)
{
register struct rt_thread *to_thread;
register rt_ubase_t highest_ready_priority;
#if RT_THREAD_PRIORITY_MAX > 32
register rt_ubase_t number;
number = __rt_ffs(rt_thread_ready_priority_group) - 1;
highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif
/* 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(void)
{
rt_base_t level;
struct rt_thread *to_thread;
struct rt_thread *from_thread;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* check the scheduler is enabled or not */
if (rt_scheduler_lock_nest == 0)
{
register rt_ubase_t highest_ready_priority;
#if RT_THREAD_PRIORITY_MAX <= 32
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#else
register rt_ubase_t number;
number = __rt_ffs(rt_thread_ready_priority_group) - 1;
highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#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 = (rt_uint8_t)highest_ready_priority;
from_thread = rt_current_thread;
rt_current_thread = to_thread;
RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
/* switch to new thread */
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
("[%d]switch to priority#%d "
"thread:%.*s(sp:0x%p), "
"from thread:%.*s(sp: 0x%p)\n",
rt_interrupt_nest, highest_ready_priority,
RT_NAME_MAX, to_thread->name, to_thread->sp,
RT_NAME_MAX, from_thread->name, from_thread->sp));
#ifdef RT_USING_OVERFLOW_CHECK
_rt_scheduler_stack_check(to_thread);
#endif
if (rt_interrupt_nest == 0)
{
rt_hw_context_switch((rt_uint32_t)&from_thread->sp,
(rt_uint32_t)&to_thread->sp);
}
else
{
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));
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 */
#if RT_THREAD_PRIORITY_MAX <= 32
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
RT_NAME_MAX, thread->name, thread->current_priority));
#else
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
("insert thread[%.*s], the priority: %d 0x%x %d\n",
RT_NAME_MAX,
thread->name,
thread->number,
thread->number_mask,
thread->high_mask));
#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();
#if RT_THREAD_PRIORITY_MAX <= 32
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n",
RT_NAME_MAX, thread->name,
thread->current_priority));
#else
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
("remove thread[%.*s], the priority: %d 0x%x %d\n",
RT_NAME_MAX,
thread->name,
thread->number,
thread->number_mask,
thread->high_mask));
#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(void)
{
register rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/*
* the maximal number of nest is RT_UINT16_MAX, which is big
* enough and does not check here
*/
rt_scheduler_lock_nest ++;
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
/**
* This function will unlock the thread scheduler.
*/
void rt_exit_critical(void)
{
register rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
rt_scheduler_lock_nest --;
if (rt_scheduler_lock_nest <= 0)
{
rt_scheduler_lock_nest = 0;
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_schedule();
}
else
{
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
}
/**
* Get the scheduler lock level
*
* @return the level of the scheduler lock. 0 means unlocked.
*/
rt_uint16_t rt_critical_level(void)
{
return rt_scheduler_lock_nest;
}
/*@}*/
@@ -0,0 +1,968 @@
/*
* File : slab.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2008-07-12 Bernard the first version
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-23 yi.qiu add module memory allocator
* 2010-12-18 yi.qiu fix zone release bug
*/
/*
* KERN_SLABALLOC.C - Kernel SLAB memory allocator
*
* Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
*
* This code is derived from software contributed to The DragonFly Project
* by Matthew Dillon <dillon@backplane.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of The DragonFly Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <rthw.h>
#include <rtthread.h>
#define RT_MEM_STATS
#if defined (RT_USING_HEAP) && defined (RT_USING_SLAB)
/* some statistical variable */
#ifdef RT_MEM_STATS
static rt_size_t used_mem, max_mem;
#endif
#ifdef RT_USING_HOOK
static void (*rt_malloc_hook)(void *ptr, rt_size_t size);
static void (*rt_free_hook)(void *ptr);
/**
* @addtogroup Hook
*/
/*@{*/
/**
* This function will set a hook function, which will be invoked when a memory
* block is allocated from heap memory.
*
* @param hook the hook function
*/
void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size))
{
rt_malloc_hook = hook;
}
RTM_EXPORT(rt_malloc_sethook);
/**
* This function will set a hook function, which will be invoked when a memory
* block is released to heap memory.
*
* @param hook the hook function
*/
void rt_free_sethook(void (*hook)(void *ptr))
{
rt_free_hook = hook;
}
RTM_EXPORT(rt_free_sethook);
/*@}*/
#endif
/*
* slab allocator implementation
*
* A slab allocator reserves a ZONE for each chunk size, then lays the
* chunks out in an array within the zone. Allocation and deallocation
* is nearly instantanious, and fragmentation/overhead losses are limited
* to a fixed worst-case amount.
*
* The downside of this slab implementation is in the chunk size
* multiplied by the number of zones. ~80 zones * 128K = 10MB of VM per cpu.
* In a kernel implementation all this memory will be physical so
* the zone size is adjusted downward on machines with less physical
* memory. The upside is that overhead is bounded... this is the *worst*
* case overhead.
*
* Slab management is done on a per-cpu basis and no locking or mutexes
* are required, only a critical section. When one cpu frees memory
* belonging to another cpu's slab manager an asynchronous IPI message
* will be queued to execute the operation. In addition, both the
* high level slab allocator and the low level zone allocator optimize
* M_ZERO requests, and the slab allocator does not have to pre initialize
* the linked list of chunks.
*
* XXX Balancing is needed between cpus. Balance will be handled through
* asynchronous IPIs primarily by reassigning the z_Cpu ownership of chunks.
*
* XXX If we have to allocate a new zone and M_USE_RESERVE is set, use of
* the new zone should be restricted to M_USE_RESERVE requests only.
*
* Alloc Size Chunking Number of zones
* 0-127 8 16
* 128-255 16 8
* 256-511 32 8
* 512-1023 64 8
* 1024-2047 128 8
* 2048-4095 256 8
* 4096-8191 512 8
* 8192-16383 1024 8
* 16384-32767 2048 8
* (if RT_MM_PAGE_SIZE is 4K the maximum zone allocation is 16383)
*
* Allocations >= zone_limit go directly to kmem.
*
* API REQUIREMENTS AND SIDE EFFECTS
*
* To operate as a drop-in replacement to the FreeBSD-4.x malloc() we
* have remained compatible with the following API requirements:
*
* + small power-of-2 sized allocations are power-of-2 aligned (kern_tty)
* + all power-of-2 sized allocations are power-of-2 aligned (twe)
* + malloc(0) is allowed and returns non-RT_NULL (ahc driver)
* + ability to allocate arbitrarily large chunks of memory
*/
/*
* Chunk structure for free elements
*/
typedef struct slab_chunk
{
struct slab_chunk *c_next;
} slab_chunk;
/*
* The IN-BAND zone header is placed at the beginning of each zone.
*/
typedef struct slab_zone
{
rt_int32_t z_magic; /* magic number for sanity check */
rt_int32_t z_nfree; /* total free chunks / ualloc space in zone */
rt_int32_t z_nmax; /* maximum free chunks */
struct slab_zone *z_next; /* zoneary[] link if z_nfree non-zero */
rt_uint8_t *z_baseptr; /* pointer to start of chunk array */
rt_int32_t z_uindex; /* current initial allocation index */
rt_int32_t z_chunksize; /* chunk size for validation */
rt_int32_t z_zoneindex; /* zone index */
slab_chunk *z_freechunk; /* free chunk list */
} slab_zone;
#define ZALLOC_SLAB_MAGIC 0x51ab51ab
#define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */
#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */
#define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */
#define NZONES 72 /* number of zones */
#define ZONE_RELEASE_THRESH 2 /* threshold number of zones */
static slab_zone *zone_array[NZONES]; /* linked list of zones NFree > 0 */
static slab_zone *zone_free; /* whole zones that have become free */
static int zone_free_cnt;
static int zone_size;
static int zone_limit;
static int zone_page_cnt;
/*
* Misc constants. Note that allocations that are exact multiples of
* RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module.
*/
#define MIN_CHUNK_SIZE 8 /* in bytes */
#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1)
/*
* Array of descriptors that describe the contents of each page
*/
#define PAGE_TYPE_FREE 0x00
#define PAGE_TYPE_SMALL 0x01
#define PAGE_TYPE_LARGE 0x02
struct memusage
{
rt_uint32_t type:2 ; /* page type */
rt_uint32_t size:30; /* pages allocated or offset from zone */
};
static struct memusage *memusage = RT_NULL;
#define btokup(addr) \
(&memusage[((rt_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 struct rt_semaphore heap_sem;
void *rt_page_alloc(rt_size_t npages)
{
struct rt_page_head *b, *n;
struct rt_page_head **prev;
if(npages == 0)
return RT_NULL;
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next))
{
if (b->page > npages)
{
/* splite pages */
n = b + npages;
n->next = b->next;
n->page = b->page - npages;
*prev = n;
break;
}
if (b->page == npages)
{
/* this node fit, remove this node */
*prev = b->next;
break;
}
}
/* unlock heap */
rt_sem_release(&heap_sem);
return b;
}
void rt_page_free(void *addr, rt_size_t npages)
{
struct rt_page_head *b, *n;
struct rt_page_head **prev;
RT_ASSERT(addr != RT_NULL);
RT_ASSERT((rt_uint32_t)addr % RT_MM_PAGE_SIZE == 0);
RT_ASSERT(npages != 0);
n = (struct rt_page_head *)addr;
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next))
{
RT_ASSERT(b->page > 0);
RT_ASSERT(b > n || b + b->page <= n);
if (b + b->page == n)
{
if (b + (b->page += npages) == b->next)
{
b->page += b->next->page;
b->next = b->next->next;
}
goto _return;
}
if (b == n + npages)
{
n->page = b->page + npages;
n->next = b->next;
*prev = n;
goto _return;
}
if (b > n + npages)
break;
}
n->page = npages;
n->next = b;
*prev = n;
_return:
/* unlock heap */
rt_sem_release(&heap_sem);
}
/*
* Initialize the page allocator
*/
static void rt_page_init(void *addr, rt_size_t npages)
{
RT_ASSERT(addr != RT_NULL);
RT_ASSERT(npages != 0);
rt_page_list = RT_NULL;
rt_page_free(addr, npages);
}
/**
* @ingroup SystemInit
*
* This function will init system heap
*
* @param begin_addr the beginning address of system page
* @param end_addr the end address of system page
*/
void rt_system_heap_init(void *begin_addr, void *end_addr)
{
rt_uint32_t limsize, npages;
RT_DEBUG_NOT_IN_INTERRUPT;
/* align begin and end addr to page */
heap_start = RT_ALIGN((rt_uint32_t)begin_addr, RT_MM_PAGE_SIZE);
heap_end = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_MM_PAGE_SIZE);
if (heap_start >= heap_end)
{
rt_kprintf("rt_system_heap_init, wrong address[0x%x - 0x%x]\n",
(rt_uint32_t)begin_addr, (rt_uint32_t)end_addr);
return;
}
limsize = heap_end - heap_start;
npages = limsize / RT_MM_PAGE_SIZE;
/* initialize heap semaphore */
rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO);
RT_DEBUG_LOG(RT_DEBUG_SLAB, ("heap[0x%x - 0x%x], size 0x%x, 0x%x pages\n",
heap_start, heap_end, limsize, npages));
/* init pages */
rt_page_init((void *)heap_start, npages);
/* calculate zone size */
zone_size = ZALLOC_MIN_ZONE_SIZE;
while (zone_size < ZALLOC_MAX_ZONE_SIZE && (zone_size << 1) < (limsize/1024))
zone_size <<= 1;
zone_limit = zone_size / 4;
if (zone_limit > ZALLOC_ZONE_LIMIT)
zone_limit = ZALLOC_ZONE_LIMIT;
zone_page_cnt = zone_size / RT_MM_PAGE_SIZE;
RT_DEBUG_LOG(RT_DEBUG_SLAB, ("zone size 0x%x, zone page count 0x%x\n",
zone_size, zone_page_cnt));
/* allocate memusage array */
limsize = npages * sizeof(struct memusage);
limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE);
memusage = rt_page_alloc(limsize/RT_MM_PAGE_SIZE);
RT_DEBUG_LOG(RT_DEBUG_SLAB, ("memusage 0x%x, size 0x%x\n",
(rt_uint32_t)memusage, limsize));
}
/*
* Calculate the zone index for the allocation request size and set the
* allocation request size to that particular zone's chunk size.
*/
rt_inline int zoneindex(rt_uint32_t *bytes)
{
/* unsigned for shift opt */
rt_uint32_t n = (rt_uint32_t)*bytes;
if (n < 128)
{
*bytes = n = (n + 7) & ~7;
/* 8 byte chunks, 16 zones */
return(n / 8 - 1);
}
if (n < 256)
{
*bytes = n = (n + 15) & ~15;
return(n / 16 + 7);
}
if (n < 8192)
{
if (n < 512)
{
*bytes = n = (n + 31) & ~31;
return(n / 32 + 15);
}
if (n < 1024)
{
*bytes = n = (n + 63) & ~63;
return(n / 64 + 23);
}
if (n < 2048)
{
*bytes = n = (n + 127) & ~127;
return(n / 128 + 31);
}
if (n < 4096)
{
*bytes = n = (n + 255) & ~255;
return(n / 256 + 39);
}
*bytes = n = (n + 511) & ~511;
return(n / 512 + 47);
}
if (n < 16384)
{
*bytes = n = (n + 1023) & ~1023;
return(n / 1024 + 55);
}
rt_kprintf("Unexpected byte count %d", n);
return 0;
}
/**
* @addtogroup MM
*/
/*@{*/
/**
* This function will allocate a block from system heap memory.
* - If the nbytes is less than zero,
* or
* - If there is no nbytes sized memory valid in system,
* the RT_NULL is returned.
*
* @param size the size of memory to be allocated
*
* @return the allocated memory
*/
void *rt_malloc(rt_size_t size)
{
slab_zone *z;
rt_int32_t zi;
slab_chunk *chunk;
struct memusage *kup;
/* zero size, return RT_NULL */
if (size == 0)
return RT_NULL;
#ifdef RT_USING_MODULE
if (rt_module_self() != RT_NULL)
return rt_module_malloc(size);
#endif
/*
* Handle large allocations directly. There should not be very many of
* these so performance is not a big issue.
*/
if (size >= zone_limit)
{
size = RT_ALIGN(size, RT_MM_PAGE_SIZE);
chunk = rt_page_alloc(size >> RT_MM_PAGE_BITS);
if (chunk == RT_NULL)
return RT_NULL;
/* set kup */
kup = btokup(chunk);
kup->type = PAGE_TYPE_LARGE;
kup->size = size >> RT_MM_PAGE_BITS;
RT_DEBUG_LOG(RT_DEBUG_SLAB,
("malloc a large memory 0x%x, page cnt %d, kup %d\n",
size,
size >> RT_MM_PAGE_BITS,
((rt_uint32_t)chunk - heap_start) >> RT_MM_PAGE_BITS));
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
#ifdef RT_MEM_STATS
used_mem += size;
if (used_mem > max_mem)
max_mem = used_mem;
#endif
goto done;
}
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
/*
* Attempt to allocate out of an existing zone. First try the free list,
* then allocate out of unallocated space. If we find a good zone move
* it to the head of the list so later allocations find it quickly
* (we might have thousands of zones in the list).
*
* Note: zoneindex() will panic of size is too large.
*/
zi = zoneindex(&size);
RT_ASSERT(zi < NZONES);
RT_DEBUG_LOG(RT_DEBUG_SLAB, ("try to malloc 0x%x on zone: %d\n", size, zi));
if ((z = zone_array[zi]) != RT_NULL)
{
RT_ASSERT(z->z_nfree > 0);
/* Remove us from the zone_array[] when we become 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;
}
#ifdef RT_MEM_STATS
used_mem += z->z_chunksize;
if (used_mem > max_mem)
max_mem = used_mem;
#endif
goto done;
}
/*
* If all zones are exhausted we need to allocate a new zone for this
* index.
*
* At least one subsystem, the tty code (see CROUND) expects power-of-2
* allocations to be power-of-2 aligned. We maintain compatibility by
* adjusting the base offset below.
*/
{
rt_int32_t off;
if ((z = zone_free) != RT_NULL)
{
/* remove zone from free zone list */
zone_free = z->z_next;
-- zone_free_cnt;
}
else
{
/* unlock heap, since page allocator will think about lock */
rt_sem_release(&heap_sem);
/* allocate a zone from page */
z = rt_page_alloc(zone_size / RT_MM_PAGE_SIZE);
if (z == RT_NULL)
goto fail;
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
RT_DEBUG_LOG(RT_DEBUG_SLAB, ("alloc a new zone: 0x%x\n",
(rt_uint32_t)z));
/* set message usage */
for (off = 0, kup = btokup(z); off < zone_page_cnt; off ++)
{
kup->type = PAGE_TYPE_SMALL;
kup->size = off;
kup ++;
}
}
/* clear to zero */
rt_memset(z, 0, sizeof(slab_zone));
/* offset of slab zone struct in zone */
off = sizeof(slab_zone);
/*
* Guarentee power-of-2 alignment for power-of-2-sized chunks.
* Otherwise just 8-byte align the data.
*/
if ((size | (size - 1)) + 1 == (size << 1))
off = (off + size - 1) & ~(size - 1);
else
off = (off + MIN_CHUNK_MASK) & ~MIN_CHUNK_MASK;
z->z_magic = ZALLOC_SLAB_MAGIC;
z->z_zoneindex = zi;
z->z_nmax = (zone_size - off) / size;
z->z_nfree = z->z_nmax - 1;
z->z_baseptr = (rt_uint8_t *)z + off;
z->z_uindex = 0;
z->z_chunksize = size;
chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size);
/* link to zone array */
z->z_next = zone_array[zi];
zone_array[zi] = z;
#ifdef RT_MEM_STATS
used_mem += z->z_chunksize;
if (used_mem > max_mem)
max_mem = used_mem;
#endif
}
done:
rt_sem_release(&heap_sem);
RT_OBJECT_HOOK_CALL(rt_malloc_hook, ((char *)chunk, size));
return chunk;
fail:
rt_sem_release(&heap_sem);
return RT_NULL;
}
RTM_EXPORT(rt_malloc);
/**
* This function will change the size of previously allocated memory block.
*
* @param ptr the previously allocated memory block
* @param size the new size of memory block
*
* @return the allocated memory
*/
void *rt_realloc(void *ptr, rt_size_t size)
{
void *nptr;
slab_zone *z;
struct memusage *kup;
if (ptr == RT_NULL)
return rt_malloc(size);
if (size == 0)
{
rt_free(ptr);
return RT_NULL;
}
#ifdef RT_USING_MODULE
if (rt_module_self() != RT_NULL)
return rt_module_realloc(ptr, size);
#endif
/*
* 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;
}
RTM_EXPORT(rt_realloc);
/**
* This function will contiguously allocate enough space for count objects
* that are size bytes of memory each and returns a pointer to the allocated
* memory.
*
* The allocated memory is filled with bytes of value zero.
*
* @param count number of objects to allocate
* @param size size of the objects to allocate
*
* @return pointer to allocated memory / NULL pointer if there is an error
*/
void *rt_calloc(rt_size_t count, rt_size_t size)
{
void *p;
/* allocate 'count' objects of size 'size' */
p = rt_malloc(count * size);
/* zero the memory */
if (p)
rt_memset(p, 0, count * size);
return p;
}
RTM_EXPORT(rt_calloc);
/**
* This function will release the previous allocated memory block by rt_malloc.
* The released memory block is taken back to system heap.
*
* @param ptr the address of memory which will be released
*/
void rt_free(void *ptr)
{
slab_zone *z;
slab_chunk *chunk;
struct memusage *kup;
/* free a RT_NULL pointer */
if (ptr == RT_NULL)
return ;
RT_OBJECT_HOOK_CALL(rt_free_hook, (ptr));
#ifdef RT_USING_MODULE
if(rt_module_self() != RT_NULL)
{
rt_module_free(rt_module_self(), ptr);
return;
}
#endif
/* get memory usage */
#if RT_DEBUG_SLAB
{
rt_uint32_t addr = ((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK);
RT_DEBUG_LOG(RT_DEBUG_SLAB,
("free a memory 0x%x and align to 0x%x, kup index %d\n",
(rt_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;
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
/* clear page counter */
size = kup->size;
kup->size = 0;
#ifdef RT_MEM_STATS
used_mem -= size * RT_MM_PAGE_SIZE;
#endif
rt_sem_release(&heap_sem);
RT_DEBUG_LOG(RT_DEBUG_SLAB,
("free large memory block 0x%x, page count %d\n",
(rt_uint32_t)ptr, size));
/* free this page */
rt_page_free(ptr, size);
return;
}
/* lock heap */
rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
/* zone case. get out zone. */
z = (slab_zone *)(((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK) -
kup->size * RT_MM_PAGE_SIZE);
RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
chunk = (slab_chunk *)ptr;
chunk->c_next = z->z_freechunk;
z->z_freechunk = chunk;
#ifdef RT_MEM_STATS
used_mem -= z->z_chunksize;
#endif
/*
* Bump the number of free chunks. If it becomes non-zero the zone
* must be added back onto the appropriate list.
*/
if (z->z_nfree++ == 0)
{
z->z_next = zone_array[z->z_zoneindex];
zone_array[z->z_zoneindex] = z;
}
/*
* If the zone becomes totally free, and there are other zones we
* can allocate from, move this zone to the FreeZones list. Since
* this code can be called from an IPI callback, do *NOT* try to mess
* with kernel_map here. Hysteresis will be performed at malloc() time.
*/
if (z->z_nfree == z->z_nmax &&
(z->z_next || zone_array[z->z_zoneindex] != z))
{
slab_zone **pz;
RT_DEBUG_LOG(RT_DEBUG_SLAB, ("free zone 0x%x\n",
(rt_uint32_t)z, z->z_zoneindex));
/* remove zone from zone array list */
for (pz = &zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next)
;
*pz = z->z_next;
/* reset zone */
z->z_magic = -1;
/* insert to free zone list */
z->z_next = zone_free;
zone_free = z;
++ zone_free_cnt;
/* release zone to page allocator */
if (zone_free_cnt > ZONE_RELEASE_THRESH)
{
register rt_base_t i;
z = zone_free;
zone_free = z->z_next;
-- zone_free_cnt;
/* set message usage */
for (i = 0, kup = btokup(z); i < zone_page_cnt; i ++)
{
kup->type = PAGE_TYPE_FREE;
kup->size = 0;
kup ++;
}
/* unlock heap */
rt_sem_release(&heap_sem);
/* release pages */
rt_page_free(z, zone_size / RT_MM_PAGE_SIZE);
return;
}
}
/* unlock heap */
rt_sem_release(&heap_sem);
}
RTM_EXPORT(rt_free);
#ifdef RT_MEM_STATS
void rt_memory_info(rt_uint32_t *total,
rt_uint32_t *used,
rt_uint32_t *max_used)
{
if (total != RT_NULL)
*total = heap_end - heap_start;
if (used != RT_NULL)
*used = used_mem;
if (max_used != RT_NULL)
*max_used = max_mem;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
void list_mem(void)
{
rt_kprintf("total memory: %d\n", heap_end - heap_start);
rt_kprintf("used memory : %d\n", used_mem);
rt_kprintf("maximum allocated memory: %d\n", max_mem);
}
FINSH_FUNCTION_EXPORT(list_mem, list memory usage information)
#endif
#endif
/*@}*/
#endif
@@ -0,0 +1,707 @@
/*
* File : thread.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-03-28 Bernard first version
* 2006-04-29 Bernard implement thread timer
* 2006-04-30 Bernard added THREAD_DEBUG
* 2006-05-27 Bernard fixed the rt_thread_yield bug
* 2006-06-03 Bernard fixed the thread timer init bug
* 2006-08-10 Bernard fixed the timer bug in thread_sleep
* 2006-09-03 Bernard changed rt_timer_delete to rt_timer_detach
* 2006-09-03 Bernard implement rt_thread_detach
* 2008-02-16 Bernard fixed the rt_thread_timeout bug
* 2010-03-21 Bernard change the errno of rt_thread_delay/sleep to
* RT_EOK.
* 2010-11-10 Bernard add cleanup callback function in thread exit.
* 2011-09-01 Bernard fixed rt_thread_exit issue when the current
* thread preempted, which reported by Jiaxing Lee.
* 2011-09-08 Bernard fixed the scheduling issue in rt_thread_startup.
* 2012-12-29 Bernard fixed compiling warning.
*/
#include <rtthread.h>
#include <rthw.h>
extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
extern struct rt_thread *rt_current_thread;
extern rt_list_t rt_thread_defunct;
static void rt_thread_exit(void)
{
struct rt_thread *thread;
register rt_base_t level;
/* get current thread */
thread = rt_current_thread;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* remove from schedule */
rt_schedule_remove_thread(thread);
/* change stat */
thread->stat = RT_THREAD_CLOSE;
/* remove it from timer list */
rt_timer_detach(&thread->thread_timer);
if ((rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) &&
thread->cleanup == RT_NULL)
{
rt_object_detach((rt_object_t)thread);
}
else
{
/* insert to defunct thread list */
rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
/* switch to next task */
rt_schedule();
}
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)
{
/* init thread list */
rt_list_init(&(thread->tlist));
thread->entry = (void *)entry;
thread->parameter = parameter;
/* stack init */
thread->stack_addr = stack_start;
thread->stack_size = (rt_uint16_t)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;
/* initialize cleanup function and user data */
thread->cleanup = 0;
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 initialize a thread, normally it's used to initialize a
* static thread object.
*
* @param thread the static thread object
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_start the start address of thread stack
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(stack_start != RT_NULL);
/* 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);
}
RTM_EXPORT(rt_thread_init);
/**
* This function will return self thread object
*
* @return the self thread object
*/
rt_thread_t rt_thread_self(void)
{
return rt_current_thread;
}
RTM_EXPORT(rt_thread_self);
/**
* This function will start a thread and put it to system ready queue
*
* @param thread the thread to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_startup(rt_thread_t thread)
{
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(thread->stat == RT_THREAD_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 = 1L << thread->number;
thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */
#else
thread->number_mask = 1L << thread->current_priority;
#endif
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
thread->name, thread->init_priority));
/* change thread stat */
thread->stat = RT_THREAD_SUSPEND;
/* then resume it */
rt_thread_resume(thread);
if (rt_thread_self() != RT_NULL)
{
/* do a scheduling */
rt_schedule();
}
return RT_EOK;
}
RTM_EXPORT(rt_thread_startup);
/**
* This function will detach a thread. The thread object will be removed from
* thread queue and detached/deleted from system object management.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_detach(rt_thread_t thread)
{
rt_base_t lock;
/* thread check */
RT_ASSERT(thread != RT_NULL);
/* remove from schedule */
rt_schedule_remove_thread(thread);
/* release thread timer */
rt_timer_detach(&(thread->thread_timer));
/* change stat */
thread->stat = RT_THREAD_CLOSE;
/* detach object */
rt_object_detach((rt_object_t)thread);
if (thread->cleanup != RT_NULL)
{
/* 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;
}
RTM_EXPORT(rt_thread_detach);
#ifdef RT_USING_HEAP
/**
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique
* @param entry the entry function of thread
* @param parameter the parameter of thread enter function
* @param stack_size the size of thread stack
* @param priority the priority of thread
* @param tick the time slice if there are same priority thread
*
* @return the created thread object
*/
rt_thread_t rt_thread_create(const char *name,
void (*entry)(void *parameter),
void *parameter,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
struct rt_thread *thread;
void *stack_start;
thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
name);
if (thread == RT_NULL)
return RT_NULL;
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
if (stack_start == RT_NULL)
{
/* allocate stack failure */
rt_object_delete((rt_object_t)thread);
return RT_NULL;
}
_rt_thread_init(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
return thread;
}
RTM_EXPORT(rt_thread_create);
/**
* This function will delete a thread. The thread object will be removed from
* thread queue and 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;
}
RTM_EXPORT(rt_thread_delete);
#endif
/**
* This function will let current thread yield processor, and scheduler will
* choose a highest thread to run. After yield processor, the current thread
* is still in READY state.
*
* @return RT_EOK
*/
rt_err_t rt_thread_yield(void)
{
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;
}
RTM_EXPORT(rt_thread_yield);
/**
* This function will let current thread sleep for some ticks.
*
* @param tick the sleep ticks
*
* @return RT_EOK
*/
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* 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();
/* clear error number of this thread to RT_EOK */
if (thread->error == -RT_ETIMEOUT)
thread->error = RT_EOK;
return RT_EOK;
}
/**
* This function will let current thread delay for some ticks.
*
* @param tick the delay ticks
*
* @return RT_EOK
*/
rt_err_t rt_thread_delay(rt_tick_t tick)
{
return rt_thread_sleep(tick);
}
RTM_EXPORT(rt_thread_delay);
/**
* This function will control thread behaviors according to control command.
*
* @param thread the specified thread to be controlled
* @param cmd the control command, which includes
* RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
* RT_THREAD_CTRL_STARTUP for starting a thread;
* RT_THREAD_CTRL_CLOSE for delete a thread.
* @param arg the argument of control command
*
* @return RT_EOK
*/
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;
}
RTM_EXPORT(rt_thread_control);
/**
* This function will suspend the specified thread.
*
* @param thread the thread to be suspended
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*
* @note if suspend self thread, after this function call, the
* rt_schedule() must be invoked.
*/
rt_err_t rt_thread_suspend(rt_thread_t thread)
{
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name));
if (thread->stat != RT_THREAD_READY)
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, %d\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* change thread stat */
thread->stat = RT_THREAD_SUSPEND;
rt_schedule_remove_thread(thread);
/* stop thread timer anyway */
rt_timer_stop(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
return RT_EOK;
}
RTM_EXPORT(rt_thread_suspend);
/**
* This function will resume a thread and put it to system ready queue.
*
* @param thread the thread to be resumed
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_resume(rt_thread_t thread)
{
register rt_base_t temp;
/* thread check */
RT_ASSERT(thread != RT_NULL);
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: %s\n", thread->name));
if (thread->stat != RT_THREAD_SUSPEND)
{
RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
thread->stat));
return -RT_ERROR;
}
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* remove from suspend list */
rt_list_remove(&(thread->tlist));
rt_timer_stop(&thread->thread_timer);
/* enable interrupt */
rt_hw_interrupt_enable(temp);
/* insert to schedule ready list */
rt_schedule_insert_thread(thread);
return RT_EOK;
}
RTM_EXPORT(rt_thread_resume);
/**
* This function is the timeout function for thread, normally which is invoked
* when thread is timeout to wait some resource.
*
* @param parameter the parameter of thread timeout function
*/
void rt_thread_timeout(void *parameter)
{
struct rt_thread *thread;
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();
}
RTM_EXPORT(rt_thread_timeout);
/**
* This function will find the specified thread.
*
* @param name the name of thread finding
*
* @return the found thread
*
* @note please don't invoke this function in interrupt status.
*/
rt_thread_t rt_thread_find(char *name)
{
struct rt_object_information *information;
struct rt_object *object;
struct rt_list_node *node;
extern struct rt_object_information rt_object_container[];
/* enter critical */
if (rt_thread_self() != RT_NULL)
rt_enter_critical();
/* try to find device object */
information = &rt_object_container[RT_Object_Class_Thread];
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)
{
/* leave critical */
if (rt_thread_self() != RT_NULL)
rt_exit_critical();
return (rt_thread_t)object;
}
}
/* leave critical */
if (rt_thread_self() != RT_NULL)
rt_exit_critical();
/* not found */
return RT_NULL;
}
RTM_EXPORT(rt_thread_find);
/*@}*/
@@ -0,0 +1,704 @@
/*
* File : timer.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2006-03-12 Bernard first version
* 2006-04-29 Bernard implement thread timer
* 2006-06-04 Bernard implement rt_timer_control
* 2006-08-10 Bernard fix the periodic timer bug
* 2006-09-03 Bernard implement rt_timer_detach
* 2009-11-11 LiJin add soft timer
* 2010-05-12 Bernard fix the timer check bug.
* 2010-11-02 Charlie re-implement tick overflow issue
* 2012-12-15 Bernard fix the next timeout issue in soft timer
*/
#include <rtthread.h>
#include <rthw.h>
/* hard timer list */
static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
#ifdef RT_USING_TIMER_SOFT
#ifndef RT_TIMER_THREAD_STACK_SIZE
#define RT_TIMER_THREAD_STACK_SIZE 512
#endif
#ifndef RT_TIMER_THREAD_PRIO
#define RT_TIMER_THREAD_PRIO 0
#endif
/* soft timer list */
static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static struct rt_thread timer_thread;
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
#endif
#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
static void _rt_timer_init(rt_timer_t timer,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
int i;
/* set flag */
timer->parent.flag = flag;
/* set deactivated */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = 0;
timer->init_tick = time;
/* initialize timer list */
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row[i]));
}
}
/* the fist timer always in the last row */
static rt_tick_t rt_timer_list_next_timeout(rt_list_t timer_list[])
{
struct rt_timer *timer;
if (rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
return RT_TICK_MAX;
timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
return timer->timeout_tick;
}
rt_inline void _rt_timer_remove(rt_timer_t timer)
{
int i;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_remove(&timer->row[i]);
}
}
#if RT_DEBUG_TIMER
static int rt_timer_count_height(struct rt_timer *timer)
{
int i, cnt = 0;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
if (!rt_list_isempty(&timer->row[i]))
cnt++;
}
return cnt;
}
void rt_timer_dump(rt_list_t timer_heads[])
{
rt_list_t *list;
for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL-1].next;
list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL-1];
list = list->next)
{
struct rt_timer *timer = rt_list_entry(list,
struct rt_timer,
row[RT_TIMER_SKIP_LIST_LEVEL-1]);
rt_kprintf("%d", rt_timer_count_height(timer));
}
rt_kprintf("\n");
}
#endif
/**
* @addtogroup Clock
*/
/*@{*/
/**
* This function will initialize a timer, normally this function is used to
* initialize a static timer object.
*
* @param timer the static timer object
* @param name the name of timer
* @param timeout the timeout function
* @param parameter the parameter of timeout function
* @param time the tick of timer
* @param flag the flag of timer
*/
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
/* timer check */
RT_ASSERT(timer != RT_NULL);
/* timer object initialization */
rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name);
_rt_timer_init(timer, timeout, parameter, time, flag);
}
RTM_EXPORT(rt_timer_init);
/**
* 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();
_rt_timer_remove(timer);
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_object_detach((rt_object_t)timer);
return -RT_EOK;
}
RTM_EXPORT(rt_timer_detach);
#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;
}
RTM_EXPORT(rt_timer_create);
/**
* 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();
_rt_timer_remove(timer);
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_object_delete((rt_object_t)timer);
return -RT_EOK;
}
RTM_EXPORT(rt_timer_delete);
#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)
{
int row_lvl;
rt_list_t *timer_list;
register rt_base_t level;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
/* timer check */
RT_ASSERT(timer != RT_NULL);
/* stop timer firstly */
level = rt_hw_interrupt_disable();
/* remove timer from list */
_rt_timer_remove(timer);
/* change status of timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_hw_interrupt_enable(level);
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
/*
* get timeout tick,
* the max timeout tick shall not great than RT_TICK_MAX/2
*/
RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2);
timer->timeout_tick = rt_tick_get() + timer->init_tick;
/* disable interrupt */
level = rt_hw_interrupt_disable();
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
/* insert timer to soft timer list */
timer_list = rt_soft_timer_list;
}
else
#endif
{
/* insert timer to system timer list */
timer_list = rt_timer_list;
}
row_head[0] = &timer_list[0];
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for (;row_head[row_lvl] != timer_list[row_lvl].prev;
row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
rt_list_t *p = row_head[row_lvl]->next;
/* fix up the entry pointer */
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
/* If we have two timers that timeout at the same time, it's
* preferred that the timer inserted early get called early.
* So insert the new timer to the end the the some-timeout timer
* list.
*/
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl+1] = row_head[row_lvl]+1;
}
/* Interestingly, this super simple timer insert counter works very very
* well on distributing the list height uniformly. By means of "very very
* well", I mean it beats the randomness of timer->timeout_tick very easily
* (actually, the timeout_tick is not random and easy to be attacked). */
random_nr++;
tst_nr = random_nr;
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL-1],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL-1]));
for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
else
break;
/* Shift over the bits we have tested. Works well with 1 bit and 2
* bits. */
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK+1)>>1;
}
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */
rt_hw_interrupt_enable(level);
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
/* check whether timer thread is ready */
if (timer_thread.stat != RT_THREAD_READY)
{
/* resume timer thread to check soft timer */
rt_thread_resume(&timer_thread);
rt_schedule();
}
}
#endif
return -RT_EOK;
}
RTM_EXPORT(rt_timer_start);
/**
* 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;
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
/* disable interrupt */
level = rt_hw_interrupt_disable();
_rt_timer_remove(timer);
/* enable interrupt */
rt_hw_interrupt_enable(level);
/* change stat */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
return RT_EOK;
}
RTM_EXPORT(rt_timer_stop);
/**
* This function will get or set some options of the timer
*
* @param timer the timer to be get or set
* @param cmd the control command
* @param arg the argument
*
* @return RT_EOK
*/
rt_err_t rt_timer_control(rt_timer_t timer, 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 &= ~RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_SET_PERIODIC:
timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
break;
}
return RT_EOK;
}
RTM_EXPORT(rt_timer_control);
/**
* This function will check timer list, if a timeout event happens, the
* corresponding timeout function will be invoked.
*
* @note this function shall be invoked in operating system timer interrupt.
*/
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));
current_tick = rt_tick_get();
/* disable interrupt */
level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL-1]))
{
t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX/2)
{
RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
/* remove timer from timer list firstly */
_rt_timer_remove(t);
/* call timeout function */
t->timeout_func(t->parameter);
/* re-get tick */
current_tick = rt_tick_get();
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else
break;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
}
/**
* This function will return the next timeout tick in the system.
*
* @return the next timeout tick in the system
*/
rt_tick_t rt_timer_next_timeout_tick(void)
{
return rt_timer_list_next_timeout(rt_timer_list);
}
#ifdef RT_USING_TIMER_SOFT
/**
* This function will check timer list, if a timeout event happens, the
* corresponding timeout function will be invoked.
*/
void rt_soft_timer_check(void)
{
rt_tick_t current_tick;
rt_list_t *n;
struct rt_timer *t;
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n"));
current_tick = rt_tick_get();
for (n = rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL-1].next;
n != &(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL-1]);)
{
t = rt_list_entry(n, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL-1]);
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
/* move node to the next */
n = n->next;
/* remove timer from timer list firstly */
_rt_timer_remove(t);
/* call timeout function */
t->timeout_func(t->parameter);
/* re-get tick */
current_tick = rt_tick_get();
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else break; /* not check anymore */
}
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
}
/* system timer thread entry */
static void rt_thread_timer_entry(void *parameter)
{
rt_tick_t next_timeout;
while (1)
{
/* get the next timeout tick */
next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list);
if (next_timeout == RT_TICK_MAX)
{
/* no software timer exist, suspend self. */
rt_thread_suspend(rt_thread_self());
rt_schedule();
}
else
{
rt_tick_t current_tick;
/* get current tick */
current_tick = rt_tick_get();
if ((next_timeout - current_tick) < RT_TICK_MAX/2)
{
/* get the delta timeout tick */
next_timeout = next_timeout - current_tick;
rt_thread_delay(next_timeout);
}
}
/* lock scheduler */
rt_enter_critical();
/* check software timer */
rt_soft_timer_check();
/* unlock scheduler */
rt_exit_critical();
}
}
#endif
/**
* @ingroup SystemInit
*
* This function will initialize system timer
*/
void rt_system_timer_init(void)
{
int i;
for (i = 0; i < sizeof(rt_timer_list)/sizeof(rt_timer_list[0]); i++)
{
rt_list_init(rt_timer_list+i);
}
}
/**
* @ingroup SystemInit
*
* This function will initialize system timer thread
*/
void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
int i;
for (i = 0;
i < sizeof(rt_soft_timer_list)/sizeof(rt_soft_timer_list[0]);
i++)
{
rt_list_init(rt_soft_timer_list+i);
}
/* start software timer thread */
rt_thread_init(&timer_thread,
"timer",
rt_thread_timer_entry,
RT_NULL,
&timer_thread_stack[0],
sizeof(timer_thread_stack),
RT_TIMER_THREAD_PRIO,
10);
/* startup */
rt_thread_startup(&timer_thread);
#endif
}
/*@}*/