@@ -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
|
||||
}
|
||||
|
||||
/*@}*/
|
||||
Reference in New Issue
Block a user