This commit is contained in:
Mark Qvist 2014-04-03 22:21:37 +02:00
commit c898b090dd
1049 changed files with 288572 additions and 0 deletions

221
bertos/mware/blanker.c Normal file
View file

@ -0,0 +1,221 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
* Copyright 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Display Blanker (implementation).
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "blanker.h"
#include "hw/hw_blanker.h"
#include <drv/kbd.h>
#include <drv/timer.h>
/* Time without input events before starting blanker */
#define BLK_BLANKTIMEOUT (15 * 1000) /* ms */
#warning FIXME:Revise me!
/** Keyboard event handler to listen for key presses in blanker. */
static KbdHandler blk_KbdHandler;
/** Time since last key event. */
static ticks_t blk_lastevent;
/** Display blanking function is enabled. */
static bool blk_enabled;
/** Display blanker is engaged right now. */
static bool blk_active;
static bool blk_on(void)
{
if (!blk_active)
{
blk_active = true;
BLK_LCDOFF;
}
return true;
}
static void blk_off(void)
{
if (blk_active)
{
blk_active = false;
BLK_LCDON;
}
}
void blk_retrigger(void)
{
blk_lastevent = timer_clock();
blk_off();
}
#if 0
/**
* Matrix-like screen saver effect
*/
static void blk_hack(void)
{
static signed char blk_colstart[CONFIG_LCD_COLS];
UBYTE row, col;
if (rand()%3 == 0)
{
/* Modify one column */
col = rand() % CONFIG_LCD_COLS;
blk_colstart[col] += rand() % 12 - 5;
}
for (col = 0; col < CONFIG_LCD_COLS; ++col)
{
if (blk_colstart[col] > 0)
{
--blk_colstart[col];
/* Scroll down */
for(row = CONFIG_LCD_ROWS-1; row; --row)
{
lcd_SetAddr(blk_layer, LCD_POS(col,row));
lcd_PutChar(blk_layer->Buf[LCD_POS(col,row-1)], blk_layer);
}
/* Add new kanji */
lcd_SetAddr(blk_layer, LCD_POS(col,0));
lcd_PutChar((char)(rand() % 127 + 128), blk_layer);
}
else if (blk_colstart[col] < 0)
{
++blk_colstart[col];
/* Clear tail */
for(row = 0; row < CONFIG_LCD_ROWS; ++row)
{
if (blk_layer->Buf[LCD_POS(col,row)] != ' ')
{
lcd_SetAddr(blk_layer, LCD_POS(col,row));
lcd_PutChar(' ', blk_layer);
break;
}
}
}
}
}
#endif
static keymask_t blk_handlerFunc(keymask_t key)
{
/* key used to turn off blanker */
static keymask_t offkey;
ticks_t now = timer_clock();
/* If key pressed */
if (key != 0)
{
blk_lastevent = now;
if (blk_active)
{
blk_off();
/* remember and eat key event */
offkey = key;
key = 0;
}
else if (key == offkey)
{
/* keep eating the key until released */
key = 0;
}
/* pass key through */
return key;
}
/* reset off key */
offkey = 0;
/* Blank timeout reached? */
if (now - blk_lastevent > ms_to_ticks(BLK_BLANKTIMEOUT))
{
/* Enable blanker unless already done */
if (!blk_active && !blk_on())
return 0;
#if 0
/* Do some nice visual effect */
blk_hack();
#endif /* _DEBUG */
}
return 0;
}
void blk_enable(void)
{
if (!blk_enabled)
{
blk_active = false;
blk_lastevent = timer_clock();
/* Add display blanker handler */
blk_KbdHandler.hook = blk_handlerFunc;
blk_KbdHandler.pri = 100; /* high priority */
blk_KbdHandler.flags = KHF_RAWKEYS;
kbd_addHandler(&blk_KbdHandler);
blk_enabled = true;
}
}
void blk_disable(void)
{
if (blk_enabled)
{
kbd_remHandler(&blk_KbdHandler);
blk_off();
blk_enabled = false;
}
}

45
bertos/mware/blanker.h Normal file
View file

@ -0,0 +1,45 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
* Copyright 2001 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \brief Display Blanker (implementation).
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef MWARE_BLANKER_H
#define MWARE_BLANKER_H
extern void blk_enable(void);
extern void blk_disable(void);
extern void blk_retrigger(void);
#endif /* MWARE_BLANKER_H */

2
bertos/mware/byteorder.h Normal file
View file

@ -0,0 +1,2 @@
#warning This header is OBSOLETE
#include <cpu/byteorder.h>

146
bertos/mware/cmd_hunk.h Normal file
View file

@ -0,0 +1,146 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Giovanni Bajo
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \brief Preprocessor magic to create hunks for the commands executed from the parser
*
* This module permits to create hunks for the functions that must be executed through
* RPC commands. For instance, given this code:
*
* \code
* ResultCode cmd_add(long a, long b, long* result);
* DECLARE_COMMAND_HUNK(add, (long)(long)(NIL), (long)(NIL));
* // ^ parameters ^ return values
* \endcode
*
* The macro is expanded to:
*
* \code
* ResultCode cmd_add_hunk(params argv[], params results[])
* {
* return cmd_add(argv[0].l, argv[1].l, &results[0].l);
* }
*
* const struct CmdTemplate cmd_add_template =
* {
* "add", "dd", "d", cmd_add_hunk
* };
* \endcode
*
* which is all the boilerplate needed to make the function ready for the RPC.
* The implementation uses the Boost Preprocessor Library (part of the Boost
* library, available at http://www.boost.org). The version we developed the
* code with is 1.31.
*
*
* \author Giovanni Bajo <rasky@develer.com>
*
*/
#ifndef CMD_HUNK_H
#define CMD_HUNK_H
#include "parser.h"
// Bring in the Boost Preprocess Library
#include <boost/preprocessor/library.hpp>
#define HUNK_INDEX_FOR_NIL 0
#define HUNK_INDEX_FOR_string 1
#define HUNK_INDEX_FOR_long 2
#define HUNK_ARRAY_LETTERS (3, (NIL, s, l))
#define HUNK_ARRAY_STRINGS (3, ("", "s", "d"))
// Transform int->l, float->f, etc.
#define HUNK_TYPE_LETTER(s, _, type) \
BOOST_PP_CAT(HUNK_INDEX_FOR_, type) \
/**/
#define HUNK_TRANSFORMER(_, array, elem) \
BOOST_PP_ARRAY_ELEM(elem, array) \
/**/
#define HUNK_SEQ_TRANS_ARRAY(seq, array) \
BOOST_PP_SEQ_TRANSFORM(HUNK_TRANSFORMER, array, seq) \
/**/
#define HUNK_PARAM(_, n, seq) \
args_results[n+1]. BOOST_PP_SEQ_ELEM(n, seq) \
/**/
#define HUNK_RESULT(_, n, seq) \
&args_results[n]. BOOST_PP_SEQ_ELEM(n, seq) \
/**/
#define HUNK_IDENTITY(_, dummy, x) x
#define CMD_HUNK_TEMPLATE(func) cmd_##func###_template
#define DECLARE_CMD_HUNK_2(func, name, param_types, result_types, flags) \
static ResultCode cmd_##name##_hunk(parms args_results[]) \
{ \
return cmd_##func( \
BOOST_PP_ENUM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(param_types)), HUNK_PARAM, HUNK_SEQ_TRANS_ARRAY(param_types, HUNK_ARRAY_LETTERS)) \
BOOST_PP_COMMA_IF(BOOST_PP_AND(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(param_types)), BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(result_types)))) \
BOOST_PP_ENUM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(result_types)), HUNK_RESULT, HUNK_SEQ_TRANS_ARRAY(result_types, HUNK_ARRAY_LETTERS)) \
); \
} \
const struct CmdTemplate CMD_HUNK_TEMPLATE(name) = { \
#name, \
BOOST_PP_SEQ_FOR_EACH(HUNK_IDENTITY, _, HUNK_SEQ_TRANS_ARRAY(param_types, HUNK_ARRAY_STRINGS)), \
BOOST_PP_SEQ_FOR_EACH(HUNK_IDENTITY, _, HUNK_SEQ_TRANS_ARRAY(result_types, HUNK_ARRAY_STRINGS)), \
cmd_##name##_hunk, \
flags \
} \
/**/
#define DECLARE_CMD_HUNK(func, param_types, result_types) \
DECLARE_CMD_HUNK_2(func, func, \
BOOST_PP_SEQ_TRANSFORM(HUNK_TYPE_LETTER, _, param_types), \
BOOST_PP_SEQ_TRANSFORM(HUNK_TYPE_LETTER, _, result_types), \
0) \
/**/
#define DECLARE_CMD_HUNK_NAME(func, name, param_types, result_types) \
DECLARE_CMD_HUNK_2(func, name, \
BOOST_PP_SEQ_TRANSFORM(HUNK_TYPE_LETTER, _, param_types), \
BOOST_PP_SEQ_TRANSFORM(HUNK_TYPE_LETTER, _, result_types), \
0) \
/**/
#define DECLARE_CMD_HUNK_FLAGS(func, param_types, result_types, flags) \
DECLARE_CMD_HUNK_2(func, func, \
BOOST_PP_SEQ_TRANSFORM(HUNK_TYPE_LETTER, _, param_types), \
BOOST_PP_SEQ_TRANSFORM(HUNK_TYPE_LETTER, _, result_types), \
flags) \
/**/
#endif

216
bertos/mware/event.c Normal file
View file

@ -0,0 +1,216 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Events handling implementation
*
*
* \author Giovanni Bajo <rasky@develer.com>
*/
#include "event.h"
#include "cfg/cfg_signal.h"
#include "cfg/cfg_timer.h"
#include <drv/timer.h> /* timer_clock() */
void event_hook_ignore(UNUSED_ARG(Event *, e))
{
}
void event_hook_softint(Event *e)
{
e->Ev.Int.func(e->Ev.Int.user_data);
}
void event_hook_generic(Event *e)
{
e->Ev.Gen.completed = true;
MEMORY_BARRIER;
}
#if CONFIG_KERN && CONFIG_KERN_SIGNALS
void event_hook_signal(Event *e)
{
sig_post((e)->Ev.Sig.sig_proc, (e)->Ev.Sig.sig_bit);
}
void event_hook_generic_signal(Event *e)
{
sig_postSignal(&e->Ev.Sig.sig, e->Ev.Sig.sig_proc, e->Ev.Sig.sig_bit);
}
/*
* Custom event hook to notify the completion of a event monitored via
* event_select().
*/
static void event_hook_generic_multiple_signal(Event *e)
{
sig_post(e->Ev.Sig.sig_proc, e->Ev.Sig.sig_bit);
}
/*
* Custom timer hook to notify the timeout of a event_waitTimeout().
*/
static void event_hook_generic_timeout_signal(void *arg)
{
Event *e = (Event *)arg;
sig_postSignal(&e->Ev.Sig.sig, e->Ev.Sig.sig_proc, SIG_TIMEOUT);
}
/*
* event_waitTimeout() slow path: this function put the current process to
* sleep until the event is notified. The timeout is managed using the custom
* timer hook event_hook_generic_timeout_signal(): if the timeout expires the
* signal SIG_TIMEOUT is notified via sig_post() to the sigmask embedded in the
* event.
*
* The custom timer hook is required because the default timer's behaviour is
* to use the process's sigmask to notify the completion of an event, that is
* not suitable for this case, because we're sleeping on the event's sigmask
* instead.
*/
static NOINLINE bool event_waitTimeoutSlowPath(Event *e, ticks_t timeout)
{
bool ret;
e->Ev.Sig.sig_proc = proc_current();
ret = (sig_waitTimeoutSignal(&e->Ev.Sig.sig,
EVENT_GENERIC_SIGNAL, timeout,
event_hook_generic_timeout_signal, e) & SIG_TIMEOUT) ?
false : true;
return ret;
}
bool event_waitTimeout(Event *e, ticks_t timeout)
{
/*
* Fast path: check if the event already happened and return
* immediately in this case.
*/
if (sig_checkSignal(&e->Ev.Sig.sig,
EVENT_GENERIC_SIGNAL) == EVENT_GENERIC_SIGNAL)
return true;
return event_waitTimeoutSlowPath(e, timeout);
}
/*
* event_select() slow path: this function handles the case when any event was
* not yet notified, so it takes care of making the current process to sleep on
* the list of events, mapping them to a different signal bit and issuing a
* call to sig_waitTimeout() using the process's sigmask.
*/
static NOINLINE int event_selectSlowPath(Event **evs, int n, ticks_t timeout)
{
sigmask_t mask = (1 << n) - 1;
int i;
for (i = 0; i < n; i++)
{
Event *e = evs[i];
/* Map each event to a distinct signal bit */
e->Ev.Sig.sig_proc = proc_current();
e->Ev.Sig.sig_bit = 1 << i;
e->action = event_hook_generic_multiple_signal;
}
IRQ_ENABLE;
mask = timeout ? sig_waitTimeout(mask, timeout) : sig_wait(mask);
if (mask & SIG_TIMEOUT)
return -1;
return UINT8_LOG2(mask);
}
int event_select(Event **evs, int n, ticks_t timeout)
{
int i;
ASSERT(n <= SIG_USER_MAX);
IRQ_DISABLE;
/* Fast path: check if one of the event already happened */
for (i = 0; i < n; i++)
{
Event *e = evs[i];
if (__sig_checkSignal(&e->Ev.Sig.sig,
EVENT_GENERIC_SIGNAL) == EVENT_GENERIC_SIGNAL)
{
IRQ_ENABLE;
return i;
}
}
/* Otherwise, fallback to the slow path */
return event_selectSlowPath(evs, n, timeout);
}
#else /* !(CONFIG_KERN && CONFIG_KERN_SIGNALS) */
bool event_waitTimeout(Event *e, ticks_t timeout)
{
ticks_t end = timer_clock() + timeout;
bool ret;
while ((ACCESS_SAFE(e->Ev.Gen.completed) == false) ||
TIMER_AFTER(timer_clock(), end))
cpu_relax();
ret = e->Ev.Gen.completed;
e->Ev.Gen.completed = false;
MEMORY_BARRIER;
return ret;
}
int event_select(Event **evs, int n, ticks_t timeout)
{
ticks_t end = timer_clock() + timeout;
int i;
while (1)
{
for (i = 0; i < n; i++)
{
Event *e = evs[i];
if (ACCESS_SAFE(e->Ev.Gen.completed) == true)
{
e->Ev.Gen.completed = false;
MEMORY_BARRIER;
return i;
}
}
if (timeout && TIMER_AFTER(timer_clock(), end))
break;
cpu_relax();
}
return -1;
}
#endif /* CONFIG_KERN && CONFIG_KERN_SIGNALS */

310
bertos/mware/event.h Normal file
View file

@ -0,0 +1,310 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999, 2001, 2003 Bernie Innocenti <bernie@codewiz.org>
* -->
*
* \defgroup event_handling Event handling module
* \ingroup core
* \{
*
* \brief Events handling
*
* This module implements a common system for executing
* a user defined action calling a hook function.
*
*
* Device drivers often need to wait the completion of some event, usually to
* allow the hardware to accomplish some asynchronous task.
*
* A common approach is to place a busy wait with a cpu_relax() loop that invokes
* the architecture-specific instructions to say that we're not doing much with
* the processor.
*
* Although technically correct, the busy loop degrades the overall system
* performance in presence of multiple processes and power consumption.
*
* With the kernel the natural way to implement such wait/complete mechanism is to
* use signals via sig_wait() and sig_post()/sig_send().
*
* However, signals in BeRTOS are only available in presence of the kernel (that
* is just a compile-time option). This means that each device driver must provide
* two different interfaces to implement the wait/complete semantic: one with the
* kernel and another without the kernel.
*
* The purpose of the completion events is to provide a generic interface to
* implement a synchronization mechanism to block the execution of code until a
* specific event happens.
*
* This interface does not depend on the presence of the kernel and it
* automatically uses the appropriate event backend to provide the same
* behaviour with or without the kernel.
*
* Example usage (wait for a generic device driver initialization):
* \code
* static Event e;
*
* static void irq_handler(void)
* {
* // Completion event has happened, resume the execution of init()
* event_do(&e);
* }
*
* static void init(void)
* {
* // Declare the generic completion event
* event_initGeneric(&e);
* // Submit the hardware initialization request
* async_hw_init();
* // Wait for the completion of the event
* event_wait(&e);
* }
* \endcode
*
* Example usage: wait multiple generic events via event_select()
* \code
* Event ev1;
* Event ev2;
*
* void event_notifier(void)
* {
* Event *evs[] = { &ev1, &ev2 };
*
* event_initGeneric(&ev1);
* event_initGeneric(&ev2);
*
* while (1)
* {
* int id = event_select(evs, countof(evs),
* ms_to_ticks(100));
* if (id < 0)
* {
* kprintf("no IRQ\n");
* continue;
* }
* kprintf("IRQ %d happened\n", id);
* }
* }
*
* void irq1_handler(void)
* {
* // do something
* ...
*
* // notify the completion of event 1
* event_do(&ev1);
* }
*
* void irq2_handler(void)
* {
* // do something
* ...
*
* // notify the completion of event 2
* event_do(&ev2);
* }
* \endcode
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "event"
*/
#ifndef KERN_EVENT_H
#define KERN_EVENT_H
#include "cfg/cfg_proc.h"
#include "cfg/cfg_signal.h"
#include "cfg/cfg_timer.h"
#include <cfg/compiler.h>
#include <cpu/power.h> /* cpu_relax() */
#if CONFIG_KERN && CONFIG_KERN_SIGNALS
#include <kern/signal.h>
/* Forward decl */
struct Process;
#endif
typedef struct Event
{
void (*action)(struct Event *);
union
{
#if CONFIG_KERN && CONFIG_KERN_SIGNALS
struct
{
struct Process *sig_proc; /* Process to be signalled */
sigbit_t sig_bit; /* Signal to send */
Signal sig; /* Local signal structure (used by generic event) */
} Sig;
#endif
struct
{
Hook func; /* Pointer to softint hook */
void *user_data; /* Data to be passed back to user hook */
} Int;
struct
{
bool completed; /* Generic event completion */
} Gen;
} Ev;
} Event;
void event_hook_ignore(Event *event);
void event_hook_signal(Event *event);
void event_hook_softint(Event *event);
void event_hook_generic(Event *event);
void event_hook_generic_signal(Event *event);
/** Initialize the event \a e as a no-op */
#define event_initNone(e) \
((e)->action = event_hook_ignore)
/** Same as event_initNone(), but returns the initialized event */
INLINE Event event_createNone(void)
{
Event e;
e.action = event_hook_ignore;
return e;
}
/** Initialize the event \a e with a software interrupt (call function \a f, with parameter \a u) */
#define event_initSoftint(e,f,u) \
((e)->action = event_hook_softint,(e)->Ev.Int.func = (f), (e)->Ev.Int.user_data = (u))
/** Same as event_initSoftint(), but returns the initialized event */
INLINE Event event_createSoftint(Hook func, void *user_data)
{
Event e;
e.action = event_hook_softint;
e.Ev.Int.func = func;
e.Ev.Int.user_data = user_data;
return e;
}
#if CONFIG_KERN && CONFIG_KERN_SIGNALS
/** Initialize the event \a e with a signal (send signal \a s to process \a p) */
#define event_initSignal(e,p,s) \
((e)->action = event_hook_signal,(e)->Ev.Sig.sig_proc = (p), (e)->Ev.Sig.sig_bit = (s))
/** Same as event_initSignal(), but returns the initialized event */
INLINE Event event_createSignal(struct Process *proc, sigbit_t bit)
{
Event e;
e.action = event_hook_signal;
e.Ev.Sig.sig_proc = proc;
e.Ev.Sig.sig_bit = bit;
return e;
}
/**
* Signal used to implement generic events.
*/
#define EVENT_GENERIC_SIGNAL SIG_SYSTEM6
/** Initialize the generic sleepable event \a e */
#define event_initGeneric(e) \
((e)->action = event_hook_generic_signal, \
(e)->Ev.Sig.sig_proc = proc_current(), \
(e)->Ev.Sig.sig_bit = EVENT_GENERIC_SIGNAL, \
(e)->Ev.Sig.sig.wait = 0, (e)->Ev.Sig.sig.recv = 0)
#else
#define event_initGeneric(e) \
((e)->action = event_hook_generic, (e)->Ev.Gen.completed = false)
#endif
/**
* Create a generic sleepable event.
*
* \return the properly initialized generic event structure.
*/
INLINE Event event_createGeneric(void)
{
Event e;
event_initGeneric(&e);
return e;
}
/**
* Wait the completion of event \a e.
*
* This function releases the CPU the application is configured to use
* the kernel, otherwise it's just a busy wait.
* \note It's forbidden to use this function inside irq handling functions.
*/
INLINE void event_wait(Event *e)
{
#if CONFIG_KERN_SIGNALS
e->Ev.Sig.sig_proc = proc_current();
sig_waitSignal(&e->Ev.Sig.sig, EVENT_GENERIC_SIGNAL);
#else
while (ACCESS_SAFE(e->Ev.Gen.completed) == false)
cpu_relax();
e->Ev.Gen.completed = false;
MEMORY_BARRIER;
#endif
}
/**
* Wait for multiple events
*
* On success return the offset in the \a evs vector of the Event that
* happened, -1 if the timeout expires.
*
* NOTE: timeout == 0 means no timeout.
*
* \attention The API is work in progress and may change in future versions.
*/
int event_select(Event **evs, int n, ticks_t timeout);
/**
* Wait the completion of event \a e or \a timeout elapses.
*
* \note It's forbidden to use this function inside irq handling functions.
*/
bool event_waitTimeout(Event *e, ticks_t timeout);
/**
* Trigger an event.
*
* Execute the callback function associated with event \a e.
*
* This function can be used also in interrupt routines, but only if the
* event was created as a signal or generic event.
*/
INLINE void event_do(struct Event *e)
{
e->action(e);
}
/** \} */
#endif /* KERN_EVENT_H */

124
bertos/mware/except.h Normal file
View file

@ -0,0 +1,124 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
* Copyright 1999 Bernie Innocenti <bernie@codewiz.org>
*
* -->
*
* \brief C++-like structured exception handling for C programs
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef MWARE_EXCEPT_H
#define MWARE_EXCEPT_H
#include <cfg/debug.h>
#include <setjmp.h>
#define EXCEPT_CONTEXTS 8
/**
* A stack of jump buffers used to record try sites
* so they can be reached from throw sites.
*
* The stack contains return points for each nested
* context. jmp_buf's are pushed into the stack at
* try points and popped out when the try block ends
* normally or when an exception is thrown.
*/
extern jmp_buf except_stack[EXCEPT_CONTEXTS];
extern int except_top;
#define PUSH_EXCEPT (ASSERT(except_top < EXCEPT_CONTEXTS), setjmp(except_stack[except_top++]))
#define POP_EXCEPT (ASSERT(except_top > 0), --except_top)
#define DO_EXCEPT (ASSERT(except_top > 0), longjmp(except_stack[--except_top], true))
/**
* Jump buffer to use when throwing an exception or aborting an operation
*
* User code can throw exceptions like this:
*
* \code
* void a_function_throwing_exceptions(void)
* {
* if (some_error_condition)
* THROW;
* }
* \endcode
*
* Catching exceptions (brackets are optional):
*
* \code
* EXCEPT_DEFINE;
*
* void a_function_catching_an_exception(void)
* {
* TRY
* {
* printf("Entered try block\n");
* a_function_throwing_exceptions();
* printf("Survived execution of critical code\n");
* }
* CATCH
* {
* printf("Exception caught!\n");
* }
* CATCH_END
* }
* \endcode
*
* Simple syntax when you don't need to do anything when catching an excaption:
*
* \code
* TRY
* printf("Entered try block\n");
* a_function_throwing_exceptions();
* printf("Survived execution of critical code\n");
* TRY_END
* \endcode
*
* You also need to declare the exception stack once in
* your global declarations:
* \code
* EXCEPT_DEFINE;
* \endcode
*/
#define TRY if (PUSH_EXCEPT) { {
#define TRY_END } POP_EXCEPT; }
#define CATCH } POP_EXCEPT; } else {
#define CATCH_END }
#define THROW DO_EXCEPT
#define EXCEPT_DEFINE \
jmp_buf except_stack[EXCEPT_CONTEXTS]; \
int except_top;
#endif /* MWARE_EXCEPT_H */

978
bertos/mware/formatwr.c Normal file
View file

@ -0,0 +1,978 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2004, 2005, 2008 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
*
* \brief Basic "printf", "sprintf" and "fprintf" formatter.
*
* This module is 100% reentrant and can be adapted to user-defined routines
* that needs formatters with special properties like different output
* channels or new format specifiers.
*
* To reduce size in applications not using real numbers or long integers
* the formatter may be compiled to exclude certain parts. This is
* controlled by giving a -D option a compilation time:
*
* \code
* -D CONFIG_PRINTF=PRINTF_FULL Full ANSI printf formatter, with some C99 extensions
* -D CONFIG_PRINTF=PRINTF_NOFLOAT Exclude support for floats
* -D CONFIG_PRINTF=PRINTF_REDUCED Simplified formatter (see below)
* -D CONFIG_PRINTF=PRINTF_NOMODIFIERS Exclude "l", "z" and "h" modifiers in reduced version
* -D CONFIG_PRINTF=PRINTF_DISABLED No formatter at all
* \endcode
*
* Code size on AVR4 with GCC 3.4.1 (-O2):
* \li PRINTF_FULL 2912byte (0xB60)
* \li PRINTF_NOFLOAT 1684byte (0x694)
* \li PRINTF_REDUCED 924byte (0x39C)
* \li PRINTF_NOMODIFIERS 416byte (0x1A0)
*
* Code/data size in words on DSP56K with CodeWarrior 6.0:
* \li PRINTF_FULL 1493/45
* \li PRINTF_NOFLOAT 795/45
* \li PRINTF_REDUCED 482/0
* \li PRINTF_NOMODIFIERS 301/0
*
* The reduced version of formatter is suitable when program size is critical
* rather than formatting power. This routine uses less than 20 bytes of
* stack space which makes it practical even in systems with less than 256
* bytes of user RAM.
*
* The only formatting specifiers supported by the reduced formatter are:
* \code
* %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
* \endcode
*
* It means that real variables are not supported as well as field
* width and precision arguments.
*/
#include "formatwr.h"
#include "cfg/cfg_formatwr.h" /* CONFIG_ macros */
#include <cfg/debug.h> /* ASSERT */
#include <cpu/pgm.h>
#include <mware/hex.h>
#ifndef CONFIG_PRINTF_N_FORMATTER
/** Disable the arcane %n formatter. */
#define CONFIG_PRINTF_N_FORMATTER 0
#endif
#ifndef CONFIG_PRINTF_OCTAL_FORMATTER
/** Disable the %o formatter. */
#define CONFIG_PRINTF_OCTAL_FORMATTER 0
#endif
/* True if we must keep a count of the number of characters we print. */
#define CONFIG_PRINTF_COUNT_CHARS (CONFIG_PRINTF_RETURN_COUNT || CONFIG_PRINTF_N_FORMATTER)
#if CONFIG_PRINTF
#if CONFIG_PRINTF > PRINTF_NOFLOAT
#include <float.h>
/* Maximum precision for floating point values */
typedef long double max_float_t;
#if CONFIG_FRMWRI_BUFSIZE
#define FRMWRI_BUFSIZE CONFIG_FRMWRI_BUFSIZE
#else
/* Conservative estimate. Max float is 3.40282e+038, so %f (but not %e or %g) must have
* space for: sign + all 38 digits + '.' + 6 decimal digits (default)
* Use a high value to avoid unexpected buffer overflows.
*/
#define FRMWRI_BUFSIZE 134
#endif
#else
#if CONFIG_FRMWRI_BUFSIZE
#define FRMWRI_BUFSIZE CONFIG_FRMWRI_BUFSIZE
#else
/*
* Conservative estimate. Should be (probably) 12 (which is the size necessary
* to represent (2^32-1) in octal plus the sign bit.
*/
#define FRMWRI_BUFSIZE 16
#endif
#endif
/* Probably useful for fancy microcontrollers such as the PIC, nobody knows. */
#ifndef MEM_ATTRIBUTE
#define MEM_ATTRIBUTE
#endif
#if CONFIG_PRINTF > PRINTF_NOMODIFIERS
#define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
#else
#define IS_SHORT (sizeof(int) == 2)
#endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
#if CONFIG_PRINTF > PRINTF_NOFLOAT
static char *float_conversion(MEM_ATTRIBUTE max_float_t value,
MEM_ATTRIBUTE short nr_of_digits,
MEM_ATTRIBUTE char *buf,
MEM_ATTRIBUTE char format_flag,
MEM_ATTRIBUTE char g_flag,
MEM_ATTRIBUTE bool alternate_flag)
{
MEM_ATTRIBUTE char *cp;
MEM_ATTRIBUTE char *buf_pointer;
MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
buf_pointer = buf;
integral_10_log = 0;
if (value >= 1)
{
while (value >= 1e11) /* To speed up things a bit */
{
value /= 1e10;
integral_10_log += 10;
}
while (value >= 10)
{
value /= 10;
integral_10_log++;
}
}
else if (value) /* Not just 0.0 */
{
while (value <= 1e-10) /* To speed up things a bit */
{
value *= 1e10;
integral_10_log -= 10;
}
while (value < 1)
{
value *= 10;
integral_10_log--;
}
}
if (g_flag)
{
if (integral_10_log < nr_of_digits && integral_10_log >= -4)
{
format_flag = 0;
nr_of_digits -= integral_10_log;
}
nr_of_digits--;
if (alternate_flag)
/* %#G - No removal of trailing zeros */
g_flag = 0;
else
/* %G - Removal of trailing zeros */
alternate_flag = true;
}
/* %e or %E */
if (format_flag)
{
dec_point_pos = 0;
}
else
{
/* Less than one... */
if (integral_10_log < 0)
{
*buf_pointer++ = '0';
if ((n = nr_of_digits) || alternate_flag)
*buf_pointer++ = '.';
i = 0;
while (--i > integral_10_log && nr_of_digits)
{
*buf_pointer++ = '0';
nr_of_digits--;
}
if (integral_10_log < (-n - 1))
/* Nothing more to do */
goto CLEAN_UP;
dec_point_pos = 1;
}
else
{
dec_point_pos = - integral_10_log;
}
}
i = dec_point_pos;
while (i <= nr_of_digits )
{
value -= (max_float_t)(n = (short)value); /* n=Digit value=Remainder */
value *= 10; /* Prepare for next shot */
*buf_pointer++ = n + '0';
if ( ! i++ && (nr_of_digits || alternate_flag))
*buf_pointer++ = '.';
}
/* Rounding possible */
if (value >= 5)
{
n = 1; /* Carry */
cp = buf_pointer - 1;
do
{
if (*cp != '.')
{
if ( (*cp += n) == ('9' + 1) )
{
*cp = '0';
n = 1;
}
else
n = 0;
}
} while (cp-- > buf);
if (n)
{
/* %e or %E */
if (format_flag)
{
cp = buf_pointer;
while (cp > buf)
{
if (*(cp - 1) == '.')
{
*cp = *(cp - 2);
cp--;
}
else
*cp = *(cp - 1);
cp--;
}
integral_10_log++;
}
else
{
cp = ++buf_pointer;
while (cp > buf)
{
*cp = *(cp - 1);
cp--;
}
}
*buf = '1';
}
}
CLEAN_UP:
/* %G - Remove trailing zeros */
if (g_flag)
{
while (*(buf_pointer - 1) == '0')
buf_pointer--;
if (*(buf_pointer - 1) == '.')
buf_pointer--;
}
/* %e or %E */
if (format_flag)
{
*buf_pointer++ = format_flag;
if (integral_10_log < 0)
{
*buf_pointer++ = '-';
integral_10_log = -integral_10_log;
}
else
*buf_pointer++ = '+';
n = 0;
buf_pointer +=10;
do
{
n++;
*buf_pointer++ = (integral_10_log % 10) + '0';
integral_10_log /= 10;
} while ( integral_10_log || n < 2 );
for ( i = n ; n > 0 ; n-- )
*(buf_pointer - 11 - i + n) = *(buf_pointer - n);
buf_pointer -= 10;
}
return (buf_pointer);
}
#endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
/**
* This routine forms the core and entry of the formatter.
*
* The conversion performed conforms to the ANSI specification for "printf".
*/
int
PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
void put_one_char(char, void *),
void *secret_pointer,
va_list ap)
{
#if CONFIG_PRINTF > PRINTF_REDUCED
MEM_ATTRIBUTE static char bad_conversion[] = "???";
MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
MEM_ATTRIBUTE int precision;
MEM_ATTRIBUTE int n;
#if CONFIG_PRINTF_COUNT_CHARS
MEM_ATTRIBUTE int nr_of_chars;
#endif
MEM_ATTRIBUTE int field_width;
MEM_ATTRIBUTE char format_flag;
enum PLUS_SPACE_FLAGS {
PSF_NONE, PSF_PLUS, PSF_MINUS
};
enum DIV_FACTOR {
DIV_DEC, DIV_HEX,
#if CONFIG_PRINTF_OCTAL_FORMATTER
DIV_OCT,
#endif
};
MEM_ATTRIBUTE struct {
enum PLUS_SPACE_FLAGS plus_space_flag : 2;
#if CONFIG_PRINTF_OCTAL_FORMATTER
enum DIV_FACTOR div_factor : 2;
#else
enum DIV_FACTOR div_factor : 1;
#endif
bool left_adjust : 1;
bool l_L_modifier : 1;
bool h_modifier : 1;
bool alternate_flag : 1;
bool nonzero_value : 1;
bool zeropad : 1;
#if CPU_HARVARD
bool progmem : 1;
#endif
} flags;
MEM_ATTRIBUTE unsigned long ulong;
#if CONFIG_PRINTF > PRINTF_NOFLOAT
MEM_ATTRIBUTE max_float_t fvalue;
#endif
MEM_ATTRIBUTE char *buf_pointer;
MEM_ATTRIBUTE char *ptr;
MEM_ATTRIBUTE const char *hex;
MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars = 0;
#endif
for (;;) /* Until full format string read */
{
while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
{
if (!format_flag)
#if CONFIG_PRINTF_RETURN_COUNT
return (nr_of_chars);
#else
return 0;
#endif
put_one_char(format_flag, secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
}
if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
{
format++;
put_one_char('%', secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
continue;
}
flags.left_adjust = false;
flags.alternate_flag = false;
flags.plus_space_flag = PSF_NONE;
flags.zeropad = false;
#if CPU_HARVARD
flags.progmem = false;
#endif
ptr = buf_pointer = &buf[0];
hex = HEX_tab;
/* check for leading '-', '+', ' ','#' or '0' flags */
for (;;)
{
switch (PGM_READ_CHAR(format))
{
case ' ':
if (flags.plus_space_flag)
goto NEXT_FLAG;
case '+':
flags.plus_space_flag = PSF_PLUS;
goto NEXT_FLAG;
case '-':
flags.left_adjust = true;
goto NEXT_FLAG;
case '#':
flags.alternate_flag = true;
goto NEXT_FLAG;
case '0':
flags.zeropad = true;
goto NEXT_FLAG;
}
break;
NEXT_FLAG:
format++;
}
/* Optional field width (may be '*') */
if (PGM_READ_CHAR(format) == '*')
{
field_width = va_arg(ap, int);
if (field_width < 0)
{
field_width = -field_width;
flags.left_adjust = true;
}
format++;
}
else
{
field_width = 0;
while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
}
if (flags.left_adjust)
flags.zeropad = false;
/* Optional precision (or '*') */
if (PGM_READ_CHAR(format) == '.')
{
if (PGM_READ_CHAR(++format) == '*')
{
precision = va_arg(ap, int);
format++;
}
else
{
precision = 0;
while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
}
}
else
precision = -1;
/* At this point, "left_adjust" is nonzero if there was
* a sign, "zeropad" is 1 if there was a leading zero
* and 0 otherwise, "field_width" and "precision"
* contain numbers corresponding to the digit strings
* before and after the decimal point, respectively,
* and "plus_space_flag" is either 0 (no flag) or
* contains a plus or space character. If there was no
* decimal point, "precision" will be -1.
*/
flags.l_L_modifier = false;
flags.h_modifier = false;
/* Optional 'l','L','z' or 'h' modifier? */
switch (PGM_READ_CHAR(format))
{
case 'l':
case 'L':
#if SIZEOF_SIZE_T == SIZEOF_LONG
case 'z':
flags.l_L_modifier = true;
#elif SIZEOF_SIZE_T == SIZEOF_INT
flags.l_L_modifier = true;
case 'z':
#endif
format++;
break;
case 'h':
flags.h_modifier = true;
format++;
break;
}
/*
* At exit from the following switch, we will emit
* the characters starting at "buf_pointer" and
* ending at "ptr"-1
*/
switch (format_flag = PGM_READ_CHAR(format++))
{
#if CONFIG_PRINTF_N_FORMATTER
case 'n':
if (sizeof(short) != sizeof(int))
{
if (sizeof(int) != sizeof(long))
{
if (h_modifier)
*va_arg(ap, short *) = nr_of_chars;
else if (flags.l_L_modifier)
*va_arg(ap, long *) = nr_of_chars;
else
*va_arg(ap, int *) = nr_of_chars;
}
else
{
if (h_modifier)
*va_arg(ap, short *) = nr_of_chars;
else
*va_arg(ap, int *) = nr_of_chars;
}
}
else
{
if (flags.l_L_modifier)
*va_arg(ap, long *) = nr_of_chars;
else
*va_arg(ap, int *) = nr_of_chars;
}
continue;
#endif
case 'c':
buf[0] = va_arg(ap, int);
ptr++;
break;
/* Custom formatter for strings in program memory. */
case 'S':
#if CPU_HARVARD
flags.progmem = true;
#endif
/* Fall trough */
case 's':
if ( !(buf_pointer = va_arg(ap, char *)) )
buf_pointer = null_pointer;
if (precision < 0)
precision = 10000;
/*
* Move `ptr' to the last character of the
* string that will be actually printed.
*/
ptr = buf_pointer;
#if CPU_HARVARD
if (flags.progmem)
{
for (n=0; pgm_read_char(ptr) && n < precision; n++)
++ptr;
}
else
#endif
for (n=0; *ptr && n < precision; n++)
++ptr;
break;
#if CONFIG_PRINTF_OCTAL_FORMATTER
case 'o':
if (flags.alternate_flag && !precision)
precision++;
#endif
case 'x':
hex = hex_tab;
case 'u':
case 'p':
case 'X':
if (format_flag == 'p')
#if defined(__AVR__) || defined(__I196__) || defined(__MSP430__) /* 16bit pointers */
ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
#else /* 32bit pointers */
ulong = (unsigned long)va_arg(ap, char *);
#endif /* 32bit pointers */
else if (flags.l_L_modifier)
ulong = va_arg(ap, unsigned long);
else if (flags.h_modifier)
ulong = (unsigned long)(unsigned short)va_arg(ap, unsigned int);
else
ulong = va_arg(ap, unsigned int);
flags.div_factor =
#if CONFIG_PRINTF_OCTAL_FORMATTER
(format_flag == 'o') ? DIV_OCT :
#endif
(format_flag == 'u') ? DIV_DEC : DIV_HEX;
flags.plus_space_flag = PSF_NONE;
goto INTEGRAL_CONVERSION;
case 'd':
case 'i':
if (flags.l_L_modifier)
ulong = (unsigned long)(long)va_arg(ap, long);
else
ulong = (unsigned long)(long)va_arg(ap, int);
/* Extract sign */
if ((signed long)ulong < 0)
{
flags.plus_space_flag = PSF_MINUS;
ulong = (unsigned long)(-((signed long)ulong));
}
flags.div_factor = DIV_DEC;
/* Now convert to digits */
INTEGRAL_CONVERSION:
ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
flags.nonzero_value = (ulong != 0);
/* No char if zero and zero precision */
if (precision != 0 || flags.nonzero_value)
{
switch (flags.div_factor)
{
case DIV_DEC:
do
*--buf_pointer = hex[ulong % 10];
while (ulong /= 10);
break;
case DIV_HEX:
do
*--buf_pointer = hex[ulong % 16];
while (ulong /= 16);
break;
#if CONFIG_PRINTF_OCTAL_FORMATTER
case DIV_OCT:
do
*--buf_pointer = hex[ulong % 8];
while (ulong /= 8);
break;
#endif
}
}
/* "precision" takes precedence */
if (precision < 0)
if (flags.zeropad)
precision = field_width - (flags.plus_space_flag != PSF_NONE);
while (precision > (int)(ptr - buf_pointer))
*--buf_pointer = '0';
if (flags.alternate_flag && flags.nonzero_value)
{
if (format_flag == 'x' || format_flag == 'X')
{
*--buf_pointer = format_flag;
*--buf_pointer = '0';
}
#if CONFIG_PRINTF_OCTAL_FORMATTER
else if ((format_flag == 'o') && (*buf_pointer != '0'))
{
*--buf_pointer = '0';
}
#endif
}
ASSERT(buf_pointer >= buf);
break;
#if CONFIG_PRINTF > PRINTF_NOFLOAT
case 'g':
case 'G':
n = 1;
format_flag -= 2;
if (! precision)
{
precision = 1;
}
goto FLOATING_CONVERSION;
case 'f':
format_flag = 0;
case 'e':
case 'E':
n = 0;
FLOATING_CONVERSION:
if (precision < 0)
{
precision = 6;
}
if (sizeof(double) != sizeof(max_float_t))
{
fvalue = flags.l_L_modifier ?
va_arg(ap,max_float_t) : va_arg(ap,double);
}
else
fvalue = va_arg(ap,max_float_t);
if (fvalue < 0)
{
flags.plus_space_flag = PSF_MINUS;
fvalue = -fvalue;
}
ptr = float_conversion (fvalue,
(short)precision,
buf_pointer += field_width,
format_flag,
(char)n,
flags.alternate_flag);
if (flags.zeropad)
{
precision = field_width - (flags.plus_space_flag != PSF_NONE);
while (precision > ptr - buf_pointer)
*--buf_pointer = '0';
}
break;
#endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
case '\0': /* Really bad place to find NUL in */
format--;
default:
/* Undefined conversion! */
ptr = buf_pointer = bad_conversion;
ptr += sizeof(bad_conversion) - 1;
break;
}
/*
* This part emittes the formatted string to "put_one_char".
*/
/* If field_width == 0 then nothing should be written. */
precision = ptr - buf_pointer;
if ( precision > field_width)
{
n = 0;
}
else
{
n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
}
/* emit any leading pad characters */
if (!flags.left_adjust)
while (--n >= 0)
{
put_one_char(' ', secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
}
/* emit flag characters (if any) */
if (flags.plus_space_flag)
{
put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
}
#if CPU_HARVARD
if (flags.progmem)
{
while (--precision >= 0)
{
put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
}
}
else
#endif /* CPU_HARVARD */
{
/* emit the string itself */
while (--precision >= 0)
{
put_one_char(*buf_pointer++, secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
}
}
/* emit trailing space characters */
if (flags.left_adjust)
while (--n >= 0)
{
put_one_char(' ', secret_pointer);
#if CONFIG_PRINTF_COUNT_CHARS
nr_of_chars++;
#endif
}
}
#else /* PRINTF_REDUCED starts here */
#if CONFIG_PRINTF > PRINTF_NOMODIFIERS
bool l_modifier, h_modifier;
unsigned long u_val, div_val;
#else
unsigned int u_val, div_val;
#endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
char format_flag;
unsigned int nr_of_chars, base;
char outChar;
char *ptr;
nr_of_chars = 0;
for (;;) /* Until full format string read */
{
while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
{
if (!format_flag)
return (nr_of_chars);
put_one_char(format_flag, secret_pointer);
nr_of_chars++;
}
#if CONFIG_PRINTF > PRINTF_NOMODIFIERS
/*
* Optional 'l', 'z' or 'h' modifiers?
*/
l_modifier = h_modifier = false;
switch (PGM_READ_CHAR(format))
{
case 'l':
#if SIZEOF_SIZE_T == SIZEOF_LONG
case 'z':
l_modifier = true;
#elif SIZEOF_SIZE_T == SIZEOF_INT
l_modifier = true;
case 'z':
#endif
format++;
break;
case 'h':
h_modifier = true;
format++;
break;
}
#endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
switch (format_flag = PGM_READ_CHAR(format++))
{
case 'c':
format_flag = va_arg(ap, int);
default:
put_one_char(format_flag, secret_pointer);
nr_of_chars++;
continue;
case 's':
ptr = va_arg(ap, char *);
while ((format_flag = *ptr++))
{
put_one_char(format_flag, secret_pointer);
nr_of_chars++;
}
continue;
case 'o':
base = 8;
if (IS_SHORT)
div_val = 0x8000;
else
div_val = 0x40000000;
goto CONVERSION_LOOP;
case 'd':
base = 10;
if (IS_SHORT)
div_val = 10000;
else
div_val = 1000000000;
goto CONVERSION_LOOP;
case 'X':
case 'x':
base = 16;
if (IS_SHORT)
div_val = 0x1000;
else
div_val = 0x10000000;
CONVERSION_LOOP:
#if CONFIG_PRINTF > PRINTF_NOMODIFIERS
if (h_modifier)
{
if (format_flag == 'd')
u_val = (short)va_arg(ap, int);
else
u_val = (unsigned short)va_arg(ap, int);
}
else if (l_modifier)
u_val = va_arg(ap, long);
else
{
if (format_flag == 'd')
u_val = va_arg(ap, int);
else
u_val = va_arg(ap, unsigned int);
}
#else /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
u_val = va_arg(ap,int);
#endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
if (format_flag == 'd')
{
if (((int)u_val) < 0)
{
u_val = - u_val;
put_one_char('-', secret_pointer);
nr_of_chars++;
}
}
while (div_val > 1 && div_val > u_val)
{
div_val /= base;
}
do
{
outChar = (u_val / div_val) + '0';
if (outChar > '9')
{
if (format_flag == 'x')
outChar += 'a'-'9'-1;
else
outChar += 'A'-'9'-1;
}
put_one_char(outChar, secret_pointer);
nr_of_chars++;
u_val %= div_val;
div_val /= base;
}
while (div_val);
} /* end switch(format_flag...) */
}
#endif /* CONFIG_PRINTF > PRINTF_REDUCED */
}
#endif /* CONFIG_PRINTF */

89
bertos/mware/formatwr.h Normal file
View file

@ -0,0 +1,89 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
*
* \brief Basic "printf", "sprintf" and "fprintf" formatter.
*
* $WIZ$ module_name = "formatwr"
* $WIZ$ module_configuration = "bertos/cfg/cfg_formatwr.h"
* $WIZ$ module_depends = "hex"
* $WIZ$ module_harvard = "both"
*/
#ifndef MWARE_FORMATWR_H
#define MWARE_FORMATWR_H
#include "cfg/cfg_formatwr.h"
#include <cpu/attr.h> /* CPU_HARVARD */
#include <stdarg.h> /* va_list */
/**
* \name _formatted_write() configuration
* $WIZ$ printf_list = "PRINTF_DISABLED", "PRINTF_NOMODIFIERS", "PRINTF_REDUCED", "PRINTF_NOFLOAT", "PRINTF_FULL"
* \{
*/
#define PRINTF_DISABLED 0
#define PRINTF_NOMODIFIERS 1
#define PRINTF_REDUCED 2
#define PRINTF_NOFLOAT 3
#define PRINTF_FULL 4
/* \} */
#ifndef CONFIG_PRINTF_RETURN_COUNT
/** Enable/disable _formatted_write return value */
#define CONFIG_PRINTF_RETURN_COUNT 1
#endif
int
_formatted_write(
const char *format,
void put_char_func(char c, void *user_data),
void *user_data,
va_list ap);
#if CPU_HARVARD
#include <cpu/pgm.h>
int _formatted_write_P(
const char * PROGMEM format,
void put_char_func(char c, void *user_data),
void *user_data,
va_list ap);
#endif /* CPU_HARVARD */
int sprintf_testSetup(void);
int sprintf_testRun(void);
int sprintf_testTearDown(void);
#endif /* MWARE_FORMATWR_H */

41
bertos/mware/hex.c Normal file
View file

@ -0,0 +1,41 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2005 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Poor man's hex arrays (implementation).
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "hex.h"
const char hex_tab[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
const char HEX_tab[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };

45
bertos/mware/hex.h Normal file
View file

@ -0,0 +1,45 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Poor man's hex arrays (implementation).
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef MWARE_HEX_H
#define MWARE_HEX_H
extern const char hex_tab[16];
extern const char HEX_tab[16];
#endif /* MWARE_HEX_H */

161
bertos/mware/ini_reader.c Normal file
View file

@ -0,0 +1,161 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Ini file reader module.
*
* \author Luca Ottaviano <lottaviano@develer.com>
*/
#include "ini_reader.h"
#include "cfg/cfg_ini_reader.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
/*
* Returns when the line containing the section is found.
* The file pointer is positioned at the start of the next line.
* Returns EOF if no section was found, 0 otherwise.
*/
static int findSection(KFile *fd, const char *section, size_t section_len, char *line, size_t size)
{
while (kfile_gets(fd, line, size) != EOF)
{
char *ptr = line;
unsigned i;
/* accept only sections that begin at first char */
if (*ptr++ != '[')
continue;
/* find the end-of-section character */
for (i = 0; i < size && *ptr != ']'; ++i, ++ptr)
;
/* The found section could be long that our section key */
if (section_len != i)
continue;
/* did we find the correct section? */
if(strncmp(&line[1], section, section_len))
continue;
else
return 0;
}
return EOF;
}
/*
* Fills the argument with the key found in line
*/
static char *getKey(const char *line, char *key, size_t size)
{
/* null-terminated string */
while (isspace((unsigned char)*line))
++line;
int i = 0;
while (*line != '=' && !isspace((unsigned char)*line) && size)
{
key[i++] = *line;
++line;
--size;
}
size ? (key[i] = '\0') : (key[i-1] = '\0');
return key;
}
/*
* Fills the argument with the value found in line.
*/
static char *getValue(const char *line, char *value, size_t size)
{
while (*line++ != '=')
;
while (isspace((unsigned char)*line))
++line;
int i = 0;
while (*line && size)
{
value[i++] = *line++;
--size;
}
size ? (value[i] = '\0') : (value[i-1] = '\0');
return value;
}
/**
* Look for key inside a section.
*
* The function reads lines from input file. It fills the line parameter to allow splitting
* the key-value couple. It returns with error if a new section begins and no key was found.
* \return 0 if key was found, EOF on errors.
*/
static int findKey(KFile *fd, const char *key, char *line, size_t size)
{
int err;
do
{
err = kfile_gets(fd, line, size);
char curr_key[30];
getKey(line, curr_key, 30);
/* check key */
if (!strcmp(curr_key, key))
return 0;
}
while (err != EOF && *line != '[');
return EOF;
}
/*
* On errors, the function returns EOF and fills the buffer with the default value.
*/
int ini_getString(KFile *fd, const char *section, const char *key, const char *default_value, char *buf, size_t size)
{
char line[CONFIG_INI_MAX_LINE_LEN];
if (kfile_seek(fd, 0, KSM_SEEK_SET) == EOF)
goto error;
if (findSection(fd, section, strlen(section), line, CONFIG_INI_MAX_LINE_LEN) == EOF)
goto error;
if (findKey(fd, key, line, CONFIG_INI_MAX_LINE_LEN) == EOF)
goto error;
else
getValue(line, buf, size);
return 0;
error:
strncpy(buf, default_value, size);
if (size > 0)
buf[size - 1] = '\0';
return EOF;
}

79
bertos/mware/ini_reader.h Normal file
View file

@ -0,0 +1,79 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \defgroup ini_reader Ini file reader
* \ingroup mware
* \{
*
* \brief Ini file reader module.
*
* The format accepted is:
* - Sections must begin at beginning of line. [ Long name ] will be found only if " Long name " is specified as section name.
* - key can contain any spaces at the beginning and before '=' but not in the middle. Eg. "long key name" is not valid.
* - values will be stripped of spaces at the beginning and will run until end-of-line. Eg. "= long value" will be treated as "long value".
* - no nested sections are allowed.
* - no comments are allowed inside a line with key=value pair.
* - every line that doesn't contain a '=' or doesn't start with '[' will be ignored.
*
* \author Luca Ottaviano <lottaviano@develer.com>
*
* $WIZ$ module_name = "ini_reader"
* $WIZ$ module_configuration = "bertos/cfg/cfg_ini_reader.h"
* $WIZ$ module_depends = "kfile"
*/
#ifndef INI_READER_H
#define INI_READER_H
#include <io/kfile.h>
/**
* \brief Returns the value for the given string in char* format.
* Reads the whole input file looking for section and key and fills the provided buffer with
* the corresponding value.
* On errors, the function fills the provided buffer with the default value and returns EOF.
* \param fd An initialized KFile structure.
* \param section The section to be looked for.
* \param key The key to search for.
* \param default_value The default value.
* \param buf The buffer to be filled.
* \param size The size of the provided buffer.
* \return 0 if section and key were found, EOF on errors.
*/
int ini_getString(KFile *fd, const char *section, const char *key, const char *default_value, char *buf, size_t size);
int ini_reader_testSetup(void);
int ini_reader_testRun(void);
int ini_reader_testTearDown(void);
/** \} */ // defgroup ini_reader
#endif /* INI_READER_H */

View file

@ -0,0 +1,100 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2009 Develer S.r.l. (http://www.develer.com/)
* -->
*
* \brief Test function for ini_reader module.
*
* $test$: cp bertos/cfg/cfg_kfile.h $cfgdir/
* $test$: echo "#undef CONFIG_KFILE_GETS" >> $cfgdir/cfg_kfile.h
* $test$: echo "#define CONFIG_KFILE_GETS 1" >> $cfgdir/cfg_kfile.h
*
* \author Luca Ottaviano <lottaviano@develer.com>
*/
#include <emul/kfile_posix.h>
#include <cfg/test.h>
#include <string.h> // strcmp
#include "ini_reader.h"
const char ini_file[] = "./test/ini_reader_file.ini";
static KFilePosix kf;
int ini_reader_testSetup(void)
{
kdbg_init();
if (!kfile_posix_init(&kf, ini_file, "r"))
{
kprintf("No test file found\n");
return -1;
}
return 0;
}
int ini_reader_testRun(void)
{
char buf[30];
memset(buf, 0, 30);
ASSERT(ini_getString(&kf.fd, "First", "String", "default", buf, 30) != EOF);
ASSERT(strcmp(buf, "noot") == 0);
ASSERT(ini_getString(&kf.fd, "Second", "Val", "default", buf, 30) != EOF);
ASSERT(strcmp(buf, "2") == 0);
ASSERT(ini_getString(&kf.fd, "First", "Empty", "default", buf, 30) != EOF);
ASSERT(strcmp(buf, "") == 0);
ASSERT(ini_getString(&kf.fd, "Second", "Bar", "default", buf, 30) == EOF);
ASSERT(strcmp(buf, "default") == 0);
ASSERT(ini_getString(&kf.fd, "Foo", "Bar", "default", buf, 30) == EOF);
ASSERT(strcmp(buf, "default") == 0);
ASSERT(ini_getString(&kf.fd, "Second", "Long key", "", buf, 30) == EOF);
ASSERT(ini_getString(&kf.fd, "Second", "comment", "", buf, 30) != EOF);
ASSERT(strcmp(buf, "line with #comment") == 0);
ASSERT(ini_getString(&kf.fd, "Long section with spaces", "value", "", buf, 30) != EOF);
ASSERT(strcmp(buf, "long value") == 0);
ASSERT(ini_getString(&kf.fd, "Long section with spaces", "no_new_line", "", buf, 30) != EOF);
ASSERT(strcmp(buf, "value") == 0);
return 0;
}
int ini_reader_testTearDown(void)
{
return kfile_close(&kf.fd);
}
TEST_MAIN(ini_reader);

66
bertos/mware/messages.c Normal file
View file

@ -0,0 +1,66 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Messages for LCD.
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
*/
#include "messages.h"
/**
* Array of pointers to localized strings. Should be filled
* by localization stuff, but not for now.
*/
const char *msg_strings[MSG_COUNT] = {
0,
// TODO: add your strings here
};
/* Buffer for catalog file */
/* char msg_buf[MSG_BUFSIZE]; */
/* The following does not work (move string tables into the DMSG/CMSG segments)
* #pragma memory=dataseg(DMSG)
* #pragma memory=constseg(CMSG)
*/
/**
* Untranslated constant strings used more than once are
* grouped here to save ROM space.
*/
const char str_empty[] = "";

67
bertos/mware/messages.h Normal file
View file

@ -0,0 +1,67 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Definitions of constant string messages.
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
*/
#ifndef MWARE_MESSAGES_H
#define MWARE_MESSAGES_H
enum
{
MSG_NULL,
// TODO: add your labels here.
MSG_COUNT
};
#warning FIXME:Revise me!
#define MSG_BUFSIZE 6144 /* FIXME: how much? */
/* String tables */
/* extern const char *msg_strings const [MSG_COUNT]; */
/* extern char msg_buf[MSG_BUFSIZE]; */
/* Macros to access translated messages */
#define MSG(x) msg_strings[x]
#define PTRMSG(x) ((x) < (const_iptr_t)256 ? msg_strings[(unsigned int)(x)] : (const char *)(x))
#endif /* MWARE_MESSAGES_H */

80
bertos/mware/observer.c Normal file
View file

@ -0,0 +1,80 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Simple notifier for the observer/subject pattern (implementation)
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "observer.h"
#include <cpu/irq.h> // IRQ_DISABLE/IRQ_ENABLE
void observer_SetEvent(Observer *observer, void (*event)(int event_id, void *param))
{
observer->event = event;
}
void observer_InitSubject(Subject *subject)
{
LIST_INIT(&subject->observers);
}
void observer_Subscribe(Subject *subject, Observer *observer)
{
ATOMIC(ADDHEAD(&subject->observers, &observer->link));
}
void observer_Unsubscribe(UNUSED_ARG(Subject *,subject), Observer *observer)
{
ATOMIC(REMOVE(&observer->link));
}
void observer_notify(Subject *subject, int event_id, void *param)
{
Observer *observer;
cpu_flags_t irqstate;
IRQ_SAVE_DISABLE(irqstate);
/*
* Run over list with protection against other
* threads, but re-enable irqs in callbacks.
*/
FOREACH_NODE(observer, &subject->observers)
{
IRQ_RESTORE(irqstate);
observer->event(event_id, param);
IRQ_SAVE_DISABLE(irqstate);
}
IRQ_RESTORE(irqstate);
}

95
bertos/mware/observer.h Normal file
View file

@ -0,0 +1,95 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Simple notifier for the subject/observer pattern (interface)
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef MWARE_OBSERVER_H
#define MWARE_OBSERVER_H
#include <struct/list.h>
/**
*
* Here's a simple example:
*
* \code
* Subject kbd_driver;
*
* Observer kbd_observer;
*
* void key_pressed(int event, void *_param)
* {
* char *param = (char *)_param;
*
* if (event == EVENT_KBD_PRESSED)
* printf("You pressed %c\n", *param);
* }
*
* void register_kbd_listener(void)
* {
* observer_SetEvent(&kbd_observer, key_pressed);
* observer_Subscribe(&kbd_driver, &kbd_observer);
* }
* \endcode
*/
typedef struct Observer
{
Node link;
void (*event)(int event_id, void *param);
} Observer;
typedef struct Subject
{
/// Subscribed observers.
List observers;
} Subject;
void observer_SetEvent(Observer *observer, void (*event)(int event_id, void *param));
#define OBSERVER_INITIALIZER(callback) { { NULL, NULL }, callback }
void observer_InitSubject(Subject *subject);
/// Aggiunge un Observer all'insieme
void observer_Subscribe(Subject *subject, Observer *observer);
/// Rimuove un Observer dall'insieme
void observer_Unsubscribe(Subject *subject, Observer *observer);
/// per tutti gli elementi nel set notifica l'evento, chiamando la relativa
/// funzione event
void observer_notify(Subject *subject, int event_id, void *param);
#endif /* MWARE_OBSERVER_H */

323
bertos/mware/parser.c Normal file
View file

@ -0,0 +1,323 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2006 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \brief Channel protocol parser and commands.
*
* This file contains the channel protocol parser and
* the definition of the protocol commands. Commands are defined
* in a "CmdTemplate" type array, containing:
* - the name of the command,
* - the arguments it expects to receive,
* - the output values,
* - the name of the function implementing the command.
*
* The arguments and results are passed to command function
* using an union: the element of the union to use for each
* argument is determined by format strings present in the
* CmdTemplate table.
*
* \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
* \author Giovanni Bajo <rasky@develer.com>
*
*
*/
#include "parser.h"
#include "cfg/cfg_parser.h"
#include <io/kfile.h>
#include <struct/hashtable.h>
#include <stdlib.h> // atol(), NULL
#include <string.h> // strchr(), strcmp()
/// Hashtable hook to extract the key from a command
static const void* get_key_from_command(const void* cmd, uint8_t* length);
/// Hashtable that handles the commands that can be executed
DECLARE_HASHTABLE_STATIC(commands, CONFIG_MAX_COMMANDS_NUMBER, get_key_from_command);
/**
* \brief Tokenize one word at a time from a text.
*
* This function is similar to strtok, but does not use any implicit
* context, nor it does modify the input buffer in any form.
* The word is returned as a STL-like [begin,end) range.
*
* To extract the first word, make both begin and end point at the
* start of the text, and call the function. Then, subsequent
* calls will return the following words (assuming the begin/end
* variable are not modified between calls).
*
* \param begin Will contain the index of the first character of the word
* \param end Will contain the index of the character after the last
* character of the word
*
* \return True if a word was extracted, false if we got to the end
* of the string without extracting any word.
*/
static bool get_word(const char **begin, const char **end)
{
const char *cur = *end;
while ((*cur == ' ' || *cur == '\t') && *cur)
++cur;
*begin = cur;
while ((*cur != ' ' && *cur != '\t') && *cur)
++cur;
*end = cur;
return (*end != *begin);
}
/**
* \brief Command arguments parser.
*
* Using the format pointed by the argument fmt
* parses the input string filling the array argv
* with input parameters of the correct type.
*
* \param fmt Parameters format string.
* \param input Input string.
* \param argv Array filled with parameters.
*
* \return False in case of errors, otherwise true.
*/
static bool parseArgs(const char *fmt, const char *input, parms argv[])
{
const char *begin = input, *end = input;
while (*fmt)
{
// Extract the argument
if (!get_word(&begin, &end))
return false;
switch (*fmt)
{
case 'd':
(*argv++).l = atol(begin);
break;
case 's':
(*argv++).s = begin;
break;
default:
ASSERT2(0, "Unknown format for argument");
return false;
}
++fmt;
}
/* check if there are remaining args */
if (get_word(&begin, &end))
return false;
return true;
}
/// Hook provided by the parser for matching of command names (TAB completion) for readline
const char* parser_rl_match(UNUSED_ARG(void *,dummy), const char *word, int word_len)
{
HashIterator cur;
HashIterator end = ht_iter_end(&commands);
const char *found = NULL;
for (cur = ht_iter_begin(&commands);
!ht_iter_cmp(cur, end);
cur = ht_iter_next(cur))
{
const struct CmdTemplate* cmdp = (const struct CmdTemplate*)ht_iter_get(cur);
if (strncmp(cmdp->name, word, word_len) == 0)
{
// If there was another matching word, it means that we have a multiple
// match: then return NULL.
if (found)
return NULL;
found = cmdp->name;
}
}
return found;
}
#if CONFIG_ENABLE_COMPAT_BEHAVIOUR
bool parser_get_cmd_id(const char* line, unsigned long* ID)
{
const char *begin = line, *end = line;
char *end2;
// The first word is the ID
if (!get_word(&begin, &end))
return false;
*ID = strtoul(begin, &end2, 10);
if (end2 != end)
return false;
return true;
}
#endif
/**
* Find the template for the command contained in the text line.
* The template can be used to tokenize the command and interpret
* it.
*
* This function can be used to find out which command is contained
* in a given text line without parsing all the parameters and
* executing it.
*
* \param input Text line to be processed (ASCIIZ)
*
* \return The command template associated with the command contained
* in the line, or NULL if the command is invalid.
*/
const struct CmdTemplate* parser_get_cmd_template(const char *input)
{
const char *begin = input, *end = input;
#if CONFIG_ENABLE_COMPAT_BEHAVIOUR
// Skip the ID, and get the command
if (!get_word(&begin, &end))
return NULL;
#endif
if (!get_word(&begin, &end))
return NULL;
return (const struct CmdTemplate*)ht_find(&commands, begin, end-begin);
}
static const char *skip_to_params(const char *input, const struct CmdTemplate *cmdp)
{
const char *begin = input, *end = input;
#if CONFIG_ENABLE_COMPAT_BEHAVIOUR
// Skip the ID, and get the command
if (!get_word(&begin, &end))
return NULL;
#endif
if (!get_word(&begin, &end))
return NULL;
ASSERT2(strlen(cmdp->name) == (size_t)(end-begin), "Invalid command template specified");
ASSERT2(!strncmp(begin, cmdp->name, end-begin), "Invalid command template specified");
return end;
}
/**
* Extract the arguments for the command contained in the text line.
*
* The first argument will always be the command name, so the actual arguments
* will start at index 1.
*
* \param input Text line to be processed (ASCIIZ)
* \param cmdp Command template for this line
* \param args Will contain the extracted parameters
*
* \return True if everything OK, false in case of parsing error.
*/
bool parser_get_cmd_arguments(const char* input, const struct CmdTemplate* cmdp, parms args[CONFIG_PARSER_MAX_ARGS])
{
input = skip_to_params(input, cmdp);
if (!input)
return false;
args[0].s = cmdp->name;
if (!parseArgs(cmdp->arg_fmt, input, args + 1))
return false;
return true;
}
static const void* get_key_from_command(const void* cmd, uint8_t* length)
{
const struct CmdTemplate* c = cmd;
*length = strlen(c->name);
return c->name;
}
/**
* \brief Command input handler.
*
* Process the input, calling the requested command (if found).
*
* \param input Text line to be processed (ASCIIZ)
*
* \return true if everything is OK, false in case of errors
*/
bool parser_process_line(const char* input)
{
const struct CmdTemplate *cmdp;
parms args[CONFIG_PARSER_MAX_ARGS];
cmdp = parser_get_cmd_template(input);
if (!cmdp)
return false;
if (!parser_get_cmd_arguments(input, cmdp, args))
return false;
if (!parser_execute_cmd(cmdp, args))
return false;
return true;
}
/**
* Register a new command into the parser
*
* \param cmd Command template describing the command
* \return true if registration was successful, false otherwise
*/
bool parser_register_cmd(const struct CmdTemplate* cmd)
{
return ht_insert(&commands, cmd);
}
void parser_init(void)
{
// Initialize the hashtable used to store the command description
ht_init(&commands);
}

277
bertos/mware/parser.h Normal file
View file

@ -0,0 +1,277 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2003, 2006 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \defgroup parser Simple RPC machinery
* \ingroup mware
* \{
*
* \brief Channel protocol parser and commands.
*
* This module provides a simple text based RPC implementation.
* Often there is the need to give a command to the device and receive results
* back. Each command may have a variable number of input and output
* parameters, with variable type, and a return code which indicates if the
* command was successfully executed or not; this module provides the machinery
* to facilitate the above RPC scenario.
* You will need to write the RPC input and reply code as well as
* the definition of the commands.
*
* Commands are defined using a CmdTemplate struct containing:
* - command name: the string that will be matched by the parser;
* - command arguments: a string representing type and number of input
* arguments;
* - command output: a string representing type and number of output arguments;
* - function callback: function implementing the command.
*
* Once you have declared the commands, you need to register them in the
* parser with the function parser_register_cmd().
* You are strongly encouraged to use MAKE_CMD() (or alternatively
* MAKE_TEMPLATE()) and REGISTER_CMD() to declare and register commands.
*
* A command line can be parsed with the following steps:
* - find the corresponding command template with parser_get_cmd_template()
* - extract command arguments with parser_get_cmd_arguments()
* - execute the command with parser_execute_cmd()
*
* You can also provide interactive command line completion using
* parser_rl_match().
*
* Example:
* \code
* // Declare a buzzer command
* MAKE_CMD(beep, "d", "",
* ({
* buz_beep(args[1].l);
* RC_OK;
* }), 0)
*
* // initialize the parser
* parser_init();
* REGISTER_CMD(beep);
*
* // parse an input line
* char buf[80];
* // read line from somewhere
* rpc_get(buf);
* // now parse the line
* const struct CmdTemplate *templ;
* templ = parser_get_cmd_template(buf);
*
* // Take arguments (optionally check errors)
* parms args[PARSER_MAX_ARGS];
* parser_get_cmd_arguments(buf, templ, args);
* //Execute command
* if(!parser_execute_cmd(templ, args))
* {
* // error
* }
* // Now args contain the outputs of the function, you can send it
* // back to the caller
* rpc_reply(args)
*
* \endcode
*
* <b>Configuration file</b>: cfg_parser.h
*
* \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
* \author Giovanni Bajo <rasky@develer.com>
*
* $WIZ$ module_name = "parser"
* $WIZ$ module_configuration = "bertos/cfg/cfg_parser.h"
* $WIZ$ module_depends = "kfile", "hashtable"
*/
#ifndef MWARE_PARSER_H
#define MWARE_PARSER_H
#include "cfg/cfg_parser.h"
#include <cpu/types.h>
#include <cfg/debug.h>
/**
* Error generated by the commands through the return code.
*/
typedef enum
{
RC_ERROR = -1, ///< Reply with error.
RC_OK = 0, ///< No reply (ignore reply arguments).
RC_REPLY = 1, ///< Reply command arguments.
RC_SKIP = 2 ///< Skip following commands
} ResultCode;
/** union that contains parameters passed to and from commands */
typedef union { long l; const char *s; } parms;
/** pointer to commands */
typedef ResultCode (*CmdFuncPtr)(parms args_results[]);
/**
* Define a command that can be tokenized by the parser.
*
* The format strings are sequences of characters, one for each
* parameter/result. Valid characters are:
*
* d - a long integer, in decimal format
* s - a var string (in RAM)
*
* \note To create and fill an instance for this function, it is strongly
* advised to use \c DECLARE_CMD_HUNK (cmd_hunk.h).
*/
struct CmdTemplate
{
const char *name; ///< Name of command
const char *arg_fmt; ///< Format string for the input
const char *result_fmt; ///< Format string for the output
CmdFuncPtr func; ///< Pointer to the handler function
uint16_t flags; ///< Currently unused.
};
#define REGISTER_FUNCTION parser_register_cmd
/**
* Utility function to register a command.
*
* \param NAME Command name to register
*/
#define REGISTER_CMD(NAME) \
do { \
if (!REGISTER_FUNCTION(&cmd_ ## NAME ## _template)) \
ASSERT2(0, "Error in registering command, no space left"); \
} while (0)
/**
* Utility macro to create a command template.
*
* It requires that a callback function with name \a cmd_NAME
* is already defined.
* \param NAME Command name
* \param ARGS Input arguments
* \param RES Output arguments
* \param FLAGS Command flags
*/
#define MAKE_TEMPLATE(NAME, ARGS, RES, FLAGS) \
const struct CmdTemplate cmd_ ## NAME ## _template = \
{ \
#NAME, ARGS, RES, cmd_ ## NAME, FLAGS \
};
/**
* Utility macro to create command templates and callback functions.
*
* Example for a version command:
* \code
* MAKE_CMD(ver, "", "ddd",
* ({
* args[1].l = VERS_MAJOR;
* args[2].l = VERS_MINOR;
* args[3].l = VERS_REV;
* RC_OK;
* }), 0);
* \endcode
*
* Remember that input and output parameters start from index 1, since
* args[0] is the command itself.
* The last line is the return value of the function.
*
* \param NAME Command name matched by the parser
* \param ARGS Input arguments to the command
* \param RES Output arguments of the command
* \param BODY Command body, expressed with C 'statement expression'
* \param FLAGS Command flags
*/
#define MAKE_CMD(NAME, ARGS, RES, BODY, FLAGS) \
static ResultCode cmd_ ## NAME (parms *args) \
{ \
return (ResultCode)BODY; \
} \
MAKE_TEMPLATE(NAME, ARGS, RES, FLAGS)
/**
* Initialize the parser module
*
* \note This function must be called before any other function in this module
*/
void parser_init(void);
bool parser_register_cmd(const struct CmdTemplate* cmd);
/**
* Hook for readline to provide completion support for the commands
* registered in the parser.
*
* \note This is meant to be used with mware/readline.c. See the
* documentation there for a description of this hook.
*/
const char* parser_rl_match(void* dummy, const char* word, int word_len);
bool parser_process_line(const char* line);
/**
* Execute a command with its arguments, and fetch its results.
*
* The \a args paramenter is value-result: it provides input arguments to
* the callback function and it stores output values on return.
*
* \param templ Template of the command to be executed
* \param args Arguments for the command, and will contain the results
*
* \return False if the command returned an error, true otherwise
*/
INLINE bool parser_execute_cmd(const struct CmdTemplate* templ, parms args[CONFIG_PARSER_MAX_ARGS])
{
return (templ->func(args) == 0);
}
const struct CmdTemplate* parser_get_cmd_template(const char* line);
bool parser_get_cmd_arguments(const char* line, const struct CmdTemplate* templ, parms args[CONFIG_PARSER_MAX_ARGS]);
#if CONFIG_ENABLE_COMPAT_BEHAVIOUR
/**
* Extract the ID from the command text line.
*
* \param line Text line to be processed (ASCIIZ)
* \param ID Will contain the ID extracted.
*
* \return True if everything ok, false if there is no ID
*
*/
bool parser_get_cmd_id(const char* line, unsigned long* ID);
#endif
/** \} */ // defgroup parser
#endif /* MWARE_PARSER_H */

2
bertos/mware/pgm.h Normal file
View file

@ -0,0 +1,2 @@
#warning This header is OBSOLETE
#include <cpu/pgm.h>

497
bertos/mware/readline.c Normal file
View file

@ -0,0 +1,497 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004 Develer S.r.l. (http://www.develer.com/)
* Copyright 2004 Giovanni Bajo
* All Rights Reserved.
* -->
*
* \brief Line editing support with history
*
* Rationale for basic implementation choices:
*
* \li The history is implemented storing consecutive ASCIIZ strings within an array of memory. When
* the history is full, the first (oldest) line is cancelled and the whole buffer is memmoved to
* overwrite it and make room. while this is is obviously not the fastest algorithm (which would
* require the use of a circular buffer) it is surely good enough for this module, which does not
* aim at fast performances (line editing does not require to be blazingly fast).
*
* \li The first character in the history is always \c \\0, and it is used as a guard. By 'wasting' it
* in this way, the code actually gets much simpler in that we remove many checks when moving
* backward (\c i>0 and similar).
*
* \li While editing, the current index points to the position of the buffer which contains the
* last character typed in (exactly like a stack pointer). This also allows to simplify calculations
* and to make easier using the last byte of history.
*
* \li While editing, the current line is always kept null-terminated. This is important because
* if the user press ENTER, we must have room to add a \c \\0 to terminate the line. If the line
* is as long as the whole history buffer, there would not be space for it. By always keeping the
* \c \\0 at the end, we properly ensure this without making index checks harder.
*
* \li When removing a line from the history (see \c pop_history()), instead of updating all the
* indices we have around, we move backward the pointer to the history we use. This way, we don't
* have to update anything. This means that we keep two pointers to the history: \c real_history
* always points to the physical start, while \c history is the adjusted pointer (that is
* dereference to read/write to it).
*
* \todo Use up/down to move through history The history line will be copied to the current line,
* making sure there is room for it.
*
* \author Giovanni Bajo <rasky@develer.com>
*/
#include "readline.h"
#include <cfg/compiler.h>
#include <cfg/debug.h>
#include <stdio.h>
/// Enable compilation of the unit test code
#define DEBUG_UNIT_TEST 0
/// Enable dump of the history after each line
#define DEBUG_DUMP_HISTORY 0
/** Special keys (escape sequences converted to a single code) */
enum RL_KEYS {
SPECIAL_KEYS = 0x1000,
/*
* Three byte keys:
* #################
* UpArrow: 0x1B 0x5B 0X41
* DownArrow: 0x1B 0x5B 0X42
* RightArrow: 0x1B 0x5B 0x43
* LeftArrow: 0x1b 0x5B 0x44
* Beak(Pause): 0x1b 0x5B 0x50
*/
KEY_UP_ARROW,
KEY_DOWN_ARROW,
KEY_LEFT_ARROW,
KEY_RIGHT_ARROW,
KEY_PAUSE,
/*
* Four byte keys:
* ################
* F1: 0x1b 0x5B 0x5B 0x41
* F2: 0x1b 0x5B 0x5B 0x42
* F3: 0x1b 0x5B 0x5B 0x43
* F4: 0x1b 0x5B 0x5B 0x44
* F5: 0x1b 0x5B 0x5B 0x45
* Ins: 0x1b 0x5B 0x32 0x7E
* Home: 0x1b 0x5B 0x31 0x7E
* PgUp: 0x1b 0x5B 0x35 0x7E
* Del: 0x1b 0x5B 0x33 0x7E
* End: 0x1b 0x5B 0x34 0x7E
* PgDn: 0x1b 0x5B 0x36 0x7E
*/
KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
KEY_INS, KEY_HOME, KEY_PGUP, KEY_DEL, KEY_END, KEY_PGDN,
/*
* Five byte keys:
* ################
* F6: 0x1b 0x5B 0x31 0x37 0x7E
* F7: 0x1b 0x5B 0x31 0x38 0x7E
* F8: 0x1b 0x5B 0x31 0x39 0x7E
* F9: 0x1b 0x5B 0x32 0x30 0x7E
* F10: 0x1b 0x5B 0x32 0x31 0x7E
* F11: 0x1b 0x5B 0x32 0x33 0x7E
* F12: 0x1b 0x5B 0x32 0x34 0x7E
*/
KEY_F6, KEY_F7, KEY_F8, KEY_F9,
KEY_F10, KEY_F11, KEY_F12,
};
/** Check if \a c is a separator between words.
* \note Parameter \a c is evaluated multiple times
*/
#define IS_WORD_SEPARATOR(c) ((c) == ' ' || (c) == '\0')
/// Write the string \a txt to the IO output (without any kind of termination)
INLINE void rl_puts(const struct RLContext* ctx, const char* txt)
{
if (!ctx->put)
return;
while (*txt)
ctx->put(*txt++, ctx->put_param);
}
/// Write character \a ch to the IO output.
INLINE void rl_putc(const struct RLContext* ctx, char ch)
{
if (ctx->put)
ctx->put(ch, ctx->put_param);
}
/** Read a character from the IO into \a ch. This function also takes
* care of converting the ANSI escape sequences into one of the codes
* defined in \c RL_KEYS.
*/
static bool rl_getc(const struct RLContext* ctx, int* ch)
{
int c = ctx->get(ctx->get_param);
if (c == EOF)
{
if (ctx->clear)
ctx->clear(ctx->clear_param);
return false;
}
if (c == 0x1B)
{
// Unknown ESC sequence. Ignore it and read
// return next character.
if (ctx->get(ctx->get_param) != 0x5B)
return rl_getc(ctx, ch);
/* To be added:
* Home: 0x1b 0x5B 0x31 0x7E
* F6: 0x1b 0x5B 0x31 0x37 0x7E
* F7: 0x1b 0x5B 0x31 0x38 0x7E
* F8: 0x1b 0x5B 0x31 0x39 0x7E
* Ins: 0x1b 0x5B 0x32 0x7E
* F9: 0x1b 0x5B 0x32 0x30 0x7E
* F10: 0x1b 0x5B 0x32 0x31 0x7E
* F11: 0x1b 0x5B 0x32 0x33 0x7E
* F12: 0x1b 0x5B 0x32 0x34 0x7E
* Del: 0x1b 0x5B 0x33 0x7E
* End: 0x1b 0x5B 0x34 0x7E
* PgUp: 0x1b 0x5B 0x35 0x7E
* PgDn: 0x1b 0x5B 0x36 0x7E
*/
c = ctx->get(ctx->get_param);
switch (c)
{
case 0x41: c = KEY_UP_ARROW; break;
case 0x42: c = KEY_DOWN_ARROW; break;
case 0x43: c = KEY_RIGHT_ARROW; break;
case 0x44: c = KEY_LEFT_ARROW; break;
case 0x50: c = KEY_PAUSE; break;
case 0x5B:
c = ctx->get(ctx->get_param);
switch (c)
{
case 0x41: c = KEY_F1; break;
case 0x42: c = KEY_F2; break;
case 0x43: c = KEY_F3; break;
case 0x44: c = KEY_F4; break;
case 0x45: c = KEY_F5; break;
default: return rl_getc(ctx, ch);
}
break;
default: return rl_getc(ctx, ch);
}
}
*ch = c;
return true;
}
INLINE void beep(struct RLContext* ctx)
{
rl_putc(ctx, '\a');
}
static bool pop_history(struct RLContext* ctx, int total_len)
{
// Compute the length of the first command (including terminator).
int len = strlen(ctx->real_history+1)+1;
// (the first byte of the history should always be 0)
ASSERT(ctx->real_history[0] == '\0');
// If it is the only one in the history, do nothing
if (len == total_len)
return false;
// Overwrite the first command with the second one
memmove(ctx->real_history, ctx->real_history+len, HISTORY_SIZE-len);
// Move back the ctx->buffer pointer so that all the indices are still valid
ctx->history -= len;
return true;
}
/// Check if index \a i points to the begin of the history.
INLINE bool is_history_begin(struct RLContext* ctx, int i)
{ return ctx->history + i == ctx->real_history; }
/// Check if index \a i points to the (exclusive) end of history
INLINE bool is_history_end(struct RLContext* ctx, int i)
{ return ctx->history + i == ctx->real_history + HISTORY_SIZE; }
/// Check if index \a i points to the (exclusive) end of history, or somewhere past the end.
INLINE bool is_history_past_end(struct RLContext* ctx, int i)
{ return ctx->history + i >= ctx->real_history + HISTORY_SIZE; }
/** Insert \a num_chars characters from \a ch into the history buffer at the
* position indicated by \a curpos. If needed, remove old history to make room.
* Returns true if everything was successful, false if there was no room to
* add the characters.
* \note \a num_chars can be 0, in which case we just make sure the line is
* correctly zero-terminated (ASCIIZ format).
*/
static bool insert_chars(struct RLContext* ctx, size_t *curpos, const char* ch, int num_chars)
{
ASSERT(!is_history_past_end(ctx, *curpos));
while (is_history_past_end(ctx, *curpos+num_chars+1))
{
if (!pop_history(ctx, *curpos))
return false;
}
while (num_chars--)
ctx->history[++(*curpos)] = *ch++;
ASSERT(!is_history_past_end(ctx, *curpos + 1));
ctx->history[*curpos+1] = '\0';
return true;
}
/// Insert a single character \a ch into the buffer (with the same semantic of \c insert_chars())
static bool insert_char(struct RLContext* ctx, size_t *curpos, char ch)
{
return insert_chars(ctx, curpos, &ch, 1);
}
#if DEBUG_DUMP_HISTORY
/// Dump the internal history of a context (used only for debug purposes)
static void dump_history(struct RLContext* ctx)
{
int k;
char buf[8];
ASSERT(ctx->real_history[0] == '\0');
rl_puts(ctx, "History dump:");
rl_puts(ctx, "\r\n");
for (k = 1;
ctx->real_history + k != ctx->history + ctx->history_pos + 1;
k += strlen(&ctx->real_history[k]) + 1)
{
rl_puts(ctx, &ctx->real_history[k]);
rl_puts(ctx, "\r\n");
}
sprintf(buf, "%d\r\n", ctx->history_pos + (ctx->history - ctx->real_history));
rl_puts(ctx, buf);
}
#endif /* DEBUG_DUMP_HISTORY */
/// Complete the current word. Return false if no unambiguous completion was found
static bool complete_word(struct RLContext *ctx, size_t *curpos)
{
const char* completed_word;
size_t wstart;
// If the current character is a separator,
// there is nothing to complete
wstart = *curpos;
if (IS_WORD_SEPARATOR(ctx->history[wstart]))
{
beep(ctx);
return false;
}
// Find the separator before the current word
do
--wstart;
while (!IS_WORD_SEPARATOR(ctx->history[wstart]));
// Complete the word through the hook
completed_word = ctx->match(ctx->match_param, ctx->history + wstart + 1, *curpos - wstart);
if (!completed_word)
return false;
// Move back the terminal cursor to the separator
while (*curpos != wstart)
{
rl_putc(ctx, '\b');
--*curpos;
}
// Insert the completed command
insert_chars(ctx, curpos, completed_word, strlen(completed_word));
rl_puts(ctx, completed_word);
insert_char(ctx, curpos, ' ');
rl_putc(ctx, ' ');
return true;
}
void rl_refresh(struct RLContext* ctx)
{
rl_puts(ctx, "\r\n");
if (ctx->prompt)
rl_puts(ctx, ctx->prompt);
rl_puts(ctx, ctx->history + ctx->history_pos + 1);
}
const char* rl_readline(struct RLContext* ctx)
{
while (1)
{
char ch;
int c;
ASSERT(ctx->history - ctx->real_history + ctx->line_pos < HISTORY_SIZE);
if (!rl_getc(ctx, &c))
return NULL;
// Just ignore special keys for now
if (c > SPECIAL_KEYS)
continue;
if (c == '\t')
{
// Ask the match hook if available
if (!ctx->match)
return NULL;
complete_word(ctx, &ctx->line_pos);
continue;
}
// Backspace cancels a character, or it is ignored if at
// the start of the line
if (c == '\b')
{
if (ctx->history[ctx->line_pos] != '\0')
{
--ctx->line_pos;
rl_puts(ctx, "\b \b");
}
continue;
}
if (c == '\r' || c == '\n')
{
rl_puts(ctx, "\r\n");
break;
}
// Add a character to the buffer, if possible
ch = (char)c;
ASSERT2(ch == c, "a special key was not properly handled");
if (insert_chars(ctx, &ctx->line_pos, &ch, 1))
rl_putc(ctx, ch);
else
beep(ctx);
}
ctx->history_pos = ctx->line_pos + 1;
while (ctx->history[ctx->line_pos] != '\0')
--ctx->line_pos;
// Do not store empty lines in the history
if (ctx->line_pos == ctx->history_pos - 1)
ctx->history_pos -= 1;
#if DEBUG_DUMP_HISTORY
dump_history(ctx);
#endif
const char *buf = &ctx->history[ctx->line_pos + 1];
ctx->line_pos = ctx->history_pos;
if (ctx->prompt)
rl_puts(ctx, ctx->prompt);
insert_chars(ctx, &ctx->line_pos, NULL, 0);
// Since the current pointer now points to the separator, we need
// to return the first character
return buf;
}
#if DEBUG_UNIT_TEST
/** Perform the unit test for the readline library */
void rl_test(void);
#if HISTORY_SIZE != 32
#error This test needs HISTORY_SIZE to be set at 32
#endif
static struct RLContext test_ctx;
static char* test_getc_ptr;
static int test_getc(void* data)
{
return *test_getc_ptr++;
}
/** Perform a readline test. The function pipes the characters from \a input_buffer
* through the I/O to \c rl_readline(). After the whole string is sent, \c do_test()
* checks if the current history within the context match \a expected_history.
*/
static bool do_test(char* input_buffer, char* expected_history)
{
rl_init_ctx(&test_ctx);
rl_sethook_get(&test_ctx, test_getc, NULL);
test_getc_ptr = input_buffer;
while (*test_getc_ptr)
rl_readline(&test_ctx);
if (memcmp(test_ctx.real_history, expected_history, HISTORY_SIZE) != 0)
{
ASSERT2(0, "history compare failed");
return false;
}
return true;
}
void rl_test(void)
{
char* test1_in = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\n";
char test1_hist[HISTORY_SIZE] = "\0l\0m\0n\0o\0p\0q\0r\0s\0t\0u\0v\0w\0x\0y\0z";
if (!do_test(test1_in, test1_hist))
return;
kprintf("rl_test successful\n");
}
#endif /* DEBUG_UNIT_TEST */

120
bertos/mware/readline.h Normal file
View file

@ -0,0 +1,120 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright (C) 2004 Giovanni Bajo
* Copyright (C) 2004 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \brief Line editing support with history
*
* This file implements a kernel for line editing through a terminal, with history of the typed lines.
* Basic feature of this module:
*
* \li Abstracted from I/O. The user must provide hooks for getc and putc functions.
* \li Basic support for ANSI escape sequences for input of special codes.
* \li Support for command name completion (through a hook).
*
*
* \author Giovanni Bajo <rasky@develer.com>
*
* $WIZ$ module_name = "readline"
* $WIZ$ module_depends = "sprintf"
*/
#ifndef MWARE_READLINE_H
#define MWARE_READLINE_H
#include <cfg/compiler.h>
#include <string.h>
#define HISTORY_SIZE 32
typedef int (*getc_hook)(void* user_data);
typedef void (*putc_hook)(char ch, void* user_data);
typedef const char* (*match_hook)(void* user_data, const char* word, int word_len);
typedef void (*clear_hook)(void* user_data);
struct RLContext
{
getc_hook get;
void* get_param;
putc_hook put;
void* put_param;
match_hook match;
void* match_param;
clear_hook clear;
void* clear_param;
const char* prompt;
char real_history[HISTORY_SIZE];
char* history;
size_t history_pos;
size_t line_pos;
};
INLINE void rl_init_ctx(struct RLContext *ctx)
{
memset(ctx, 0, sizeof(*ctx));
ctx->history = ctx->real_history;
}
INLINE void rl_clear_history(struct RLContext *ctx)
{
memset(ctx->real_history, 0, sizeof(ctx->real_history));
ctx->history_pos = 0;
ctx->line_pos = ctx->history_pos;
ctx->history = ctx->real_history;
}
INLINE void rl_sethook_get(struct RLContext* ctx, getc_hook get, void* get_param)
{ ctx->get = get; ctx->get_param = get_param; }
INLINE void rl_sethook_put(struct RLContext* ctx, putc_hook put, void* put_param)
{ ctx->put = put; ctx->put_param = put_param; }
INLINE void rl_sethook_match(struct RLContext* ctx, match_hook match, void* match_param)
{ ctx->match = match; ctx->match_param = match_param; }
INLINE void rl_sethook_clear(struct RLContext* ctx, clear_hook clear, void* clear_param)
{ ctx->clear = clear; ctx->clear_param = clear_param; }
INLINE void rl_setprompt(struct RLContext* ctx, const char* prompt)
{ ctx->prompt = prompt; }
const char* rl_readline(struct RLContext* ctx);
void rl_refresh(struct RLContext* ctx);
#endif /* MWARE_READLINE_H */

113
bertos/mware/resource.c Normal file
View file

@ -0,0 +1,113 @@
#include "resource.h"
#include <mware/observer.h>
/**
* Internal structure for building a priority queue
* of processes waiting for the resource to become free.
*/
typedef struct ResourceWaiter
{
PriNode link;
struct Observer *owner;
} ResourceWaiter;
bool ResMan_Alloc(Resource *res, int pri, ResMan_time_t timeout, struct Observer *releaseRequest)
{
bool success = false;
ASSERT(releaseRequest);
sem_obtain(&res->lock);
if (res->owner == releaseRequest)
{
// Already ours
res->pri = pri;
success = true;
}
else if (!res->owner)
{
// Trivial acquire: nobody was owning the resource
res->pri = pri;
res->owner = releaseRequest;
success = true;
}
else
{
ResourceWaiter waiter;
// Setup waiter structure and enqueue it to resource
waiter.owner = releaseRequest;
waiter.link.pri = pri;
LIST_ENQUEUE(&res->queue, &waiter.link);
// Resource busy: are we eligible for preemption?
if ((res->pri < pri) && res->owner->event)
res->owner->event(EVENT_RELEASE, res);
// Wait in the queue until the timeout occurs.
do
{
sem_release(&res->lock);
// TODO: use a semaphore here instead
ResMan_sleep();
sem_obtain(&res->lock);
// Check for ownership
if (res->owner == releaseRequest)
{
success = true;
break;
}
}
while (timeout--);
// Remove pending waiter
if (!success)
REMOVE(&waiter.link.link);
}
sem_release(&res->lock);
return success;
}
void ResMan_Free(Resource *res)
{
ResourceWaiter *waiter;
sem_obtain(&res->lock);
ASSERT(res->owner);
//TODO: check for real owner calling free
// Check for new owner candidates.
if ((waiter = (ResourceWaiter *)list_remHead(&res->queue)))
{
// Transfer ownership of the resource
res->owner = waiter->owner;
res->pri = waiter->link.pri;
//ResMan_wakeup(waiter);
}
else
{
// Nobody waiting, free the resource
res->owner = NULL;
res->pri = -1;
}
sem_release(&res->lock);
}
void ResMan_Init(Resource *res)
{
res->owner = NULL;
res->pri = -1;
sem_init(&res->lock);
LIST_INIT(&res->queue);
}

105
bertos/mware/resource.h Normal file
View file

@ -0,0 +1,105 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
* All Rights Reserved.
* -->
*
* \brief TODO:
*
*
* \author Bernie Innocenti <bernie@codewiz.org>
* \author Stefano Fedrigo <aleph@develer.com>
*/
#ifndef MWARE_RESOURCE_H
#define MWARE_RESOURCE_H
#include <drv/timer.h> // time_t
#include <kern/sem.h>
#warning FIXME:Revise me!
/*
* Abstract locking primitives used by host OS.
*/
typedef Semaphore ResourceLock;
#define ResMan_sleep() timer_delay(1)
#define ResMan_time_t mtime_t
// Forward decl
struct Observer;
/**
* Hold context information for a resource such as an audio channel.
*
* Each driver registers one or more Resource instances with the
* ResMan using ResMan_Register().
*
* Clients can then allocate the resource through ResMan_Alloc()
* providing a desired priority and an Observer for asynchronous
* notification.
*
* Allocated resources can be stolen by other clients asking for a
* higher priority. ResMan notifies a preemption request by invoking
* the Observer of the current owner.
*
* The Observer callback must take whatever action is needed to
* release the resource as soon as possible to avoid blocking the
* new owner.
*/
typedef struct Resource
{
//Private
/// Control access to fields below.
Semaphore lock;
/// Pointer to current owner's observer. NULL if resource is free.
struct Observer *owner;
/// Priority of current owner (higher values mean higher priority).
int pri;
/// Queue of processes waiting to obtain the resource.
List queue;
} Resource;
/// Event sent by ResMan to owners when to request resource release.
enum { EVENT_RELEASE = 1 };
/// Try to allocate a resource \a res with priority \a pri for at most \a timeout ticks.
bool ResMan_Alloc(Resource *res, int pri, ResMan_time_t timeout, struct Observer *releaseRequest);
/// Free resource \a res. Will eventually wake-up other queued owners.
void ResMan_Free(Resource *res);
void ResMan_Init(Resource *res);
#endif /* MWARE_RESOURCE_H */

2
bertos/mware/rle.h Normal file
View file

@ -0,0 +1,2 @@
#warning This header is OBSOLETE
#include <algo/rle.h>

157
bertos/mware/sprintf.c Normal file
View file

@ -0,0 +1,157 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2002, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief sprintf() implementation based on _formatted_write()
*
* \author Bernie Innocenti <bernie@codewiz.org>
*
* $WIZ$ module_name = "sprintf"
* $WIZ$ module_depends = "formatwr"
* $WIZ$ module_harvard = "both"
*/
#include <mware/formatwr.h>
#include <cpu/pgm.h>
#include <cfg/compiler.h>
#include <stdio.h>
static void __str_put_char(char c, void *ptr)
{
/*
* This Does not work on Code Warrior. Hmm...
* *(*((char **)ptr))++ = c;
*/
**((char **)ptr) = c;
(*((char **)ptr))++;
}
static void __null_put_char(UNUSED_ARG(char, c), UNUSED_ARG(void *, ptr))
{
/* nop */
}
int PGM_FUNC(vsprintf)(char *str, const char * PGM_ATTR fmt, va_list ap)
{
int result;
if (str)
{
result = PGM_FUNC(_formatted_write)(fmt, __str_put_char, &str, ap);
/* Terminate string */
*str = '\0';
}
else
result = PGM_FUNC(_formatted_write)(fmt, __null_put_char, 0, ap);
return result;
}
int PGM_FUNC(sprintf)(char *str, const char * fmt, ...)
{
int result;
va_list ap;
va_start(ap, fmt);
result = PGM_FUNC(vsprintf)(str, fmt, ap);
va_end(ap);
return result;
}
/**
* State information for __sn_put_char()
*/
struct __sn_state
{
char *str;
size_t len;
};
/**
* formatted_write() callback used [v]snprintf().
*/
static void __sn_put_char(char c, void *ptr)
{
struct __sn_state *state = (struct __sn_state *)ptr;
if (state->len)
{
--state->len;
*state->str++ = c;
}
}
int PGM_FUNC(vsnprintf)(char *str, size_t size, const char * PGM_ATTR fmt, va_list ap)
{
int result = 0;
/* Make room for traling '\0'. */
if (size--)
{
if (str)
{
struct __sn_state state;
state.str = str;
state.len = size;
result = PGM_FUNC(_formatted_write)(fmt, __sn_put_char, &state, ap);
/* Terminate string. */
*state.str = '\0';
}
else
result = PGM_FUNC(_formatted_write)(fmt, __null_put_char, 0, ap);
}
return result;
}
int PGM_FUNC(snprintf)(char *str, size_t size, const char * fmt, ...)
{
int result;
va_list ap;
va_start(ap, fmt);
result = PGM_FUNC(vsnprintf)(str, size, fmt, ap);
va_end(ap);
return result;
}

114
bertos/mware/sprintf_test.c Normal file
View file

@ -0,0 +1,114 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2004, 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* notest: avr
* notest: arm
* \brief sprintf() implementation based on _formatted_write()
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "formatwr.h"
#include <cfg/compiler.h>
#include <cfg/test.h>
#include <cfg/debug.h>
#include <cpu/pgm.h>
#include <stdio.h>
#include <string.h> /* strcmp() */
int sprintf_testSetup(void)
{
kdbg_init();
return 0;
}
int sprintf_testRun(void)
{
char buf[256];
static const char test_string[] = "Hello, world!\n";
static const pgm_char test_string_pgm[] = "Hello, world!\n";
snprintf(buf, sizeof buf, "%s", test_string);
if (strcmp(buf, test_string) != 0)
return 1;
snprintf(buf, sizeof buf, "%S", (const wchar_t *)test_string_pgm);
if (strcmp(buf, test_string_pgm) != 0)
return 2;
#define TEST(FMT, VALUE, EXPECT) do { \
snprintf(buf, sizeof buf, FMT, VALUE); \
if (strcmp(buf, EXPECT) != 0) \
return -1; \
} while (0)
TEST("%d", 12345, "12345");
TEST("%ld", 123456789L, "123456789");
TEST("%ld", -12345678L, "-12345678");
TEST("%lu", 4294967295UL, "4294967295");
TEST("%hd", -12345, "-12345");
TEST("%hu", 65535U, "65535");
TEST("%8d", 123, " 123");
TEST("%8d", -123, " -123");
TEST("%-8d", -123, "-123 ");
TEST("%08d", -123, "-0000123");
TEST("%8.2f", -123.456, " -123.46");
TEST("%-8.2f", -123.456, "-123.46 ");
TEST("%8.0f", -123.456, " -123");
/*
* Stress tests.
*/
snprintf(buf, sizeof buf, "%s", (char *)(NULL));
if (strcmp(buf, "<NULL>") != 0)
return 3;
snprintf(buf, sizeof buf, "%k");
if (strcmp(buf, "???") != 0)
return 4;
sprintf(NULL, test_string); /* must not crash */
return 0;
}
int sprintf_testTearDown(void)
{
return 0;
}
TEST_MAIN(sprintf);

99
bertos/mware/strtol10.c Normal file
View file

@ -0,0 +1,99 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Poor man's hex arrays (implementation).
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#include "strtol10.h"
/**
* Convert a formatted base-10 ASCII number to unsigned long binary representation.
*
* Unlike the standard strtoul(), this function has an interface
* that makes it better suited for protocol parsers. It's also
* much simpler and smaller than a full featured strtoul().
*
* \param first Pointer to first byte of input range (STL-style).
* \param last Pointer to end of input range (STL-style).
* Pass NULL to parse up to the first \\0.
* \param val Pointer to converted value.
*
* \return true for success, false for failure.
*
* \see strtol10()
*/
bool strtoul10(const char *first, const char *last, unsigned long *val)
{
// Check for no input
if (*first == '\0')
return false;
*val = 0;
for(/*nop*/; first != last && *first != '\0'; ++first)
{
if ((*first < '0') || (*first > '9'))
return false;
*val = (*val * 10L) + (*first - '0');
}
return true;
}
/**
* Convert a formatted base-10 ASCII number to signed long binary representation.
*
* \see strtoul10()
*/
bool strtol10(const char *first, const char *last, long *val)
{
bool negative = false;
if (*first == '+')
++first; /* skip unary plus sign */
else if (*first == '-')
{
negative = true;
++first;
}
bool result = strtoul10(first, last, (unsigned long *)val);
if (negative)
*val = - *val;
return result;
}

65
bertos/mware/strtol10.h Normal file
View file

@ -0,0 +1,65 @@
/**
* \file
* <!--
* This file is part of BeRTOS.
*
* Bertos 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 St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2005 Develer S.r.l. (http://www.develer.com/)
*
* -->
*
* \brief Poor man's hex arrays (implementation).
*
* \author Bernie Innocenti <bernie@codewiz.org>
*/
#ifndef MWARE_STRTOL10_H
#define MWARE_STRTOL10_H
#include <cfg/compiler.h> /* bool */
bool strtoul10(const char *first, const char *last, unsigned long *val);
bool strtol10(const char *first, const char *last, long *val);
/**
* Replacement for standard library function atol().
*/
INLINE long atol(const char *str)
{
long val;
strtol10(str, NULL, &val);
return val;
}
/**
* Replacement for standard library function atoi().
*/
INLINE int atoi(const char *str)
{
return (int)atol(str);
}
#endif /* MWARE_STRTOL10_H */