mirror of
https://github.com/markqvist/OpenModem.git
synced 2025-12-15 16:39:00 -05:00
Working
This commit is contained in:
commit
c898b090dd
1049 changed files with 288572 additions and 0 deletions
221
bertos/mware/blanker.c
Normal file
221
bertos/mware/blanker.c
Normal 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
45
bertos/mware/blanker.h
Normal 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
2
bertos/mware/byteorder.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#warning This header is OBSOLETE
|
||||
#include <cpu/byteorder.h>
|
||||
146
bertos/mware/cmd_hunk.h
Normal file
146
bertos/mware/cmd_hunk.h
Normal 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
216
bertos/mware/event.c
Normal 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
310
bertos/mware/event.h
Normal 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
124
bertos/mware/except.h
Normal 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
978
bertos/mware/formatwr.c
Normal 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
89
bertos/mware/formatwr.h
Normal 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
41
bertos/mware/hex.c
Normal 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
45
bertos/mware/hex.h
Normal 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
161
bertos/mware/ini_reader.c
Normal 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
79
bertos/mware/ini_reader.h
Normal 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 */
|
||||
100
bertos/mware/ini_reader_test.c
Normal file
100
bertos/mware/ini_reader_test.c
Normal 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
66
bertos/mware/messages.c
Normal 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
67
bertos/mware/messages.h
Normal 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
80
bertos/mware/observer.c
Normal 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
95
bertos/mware/observer.h
Normal 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
323
bertos/mware/parser.c
Normal 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
277
bertos/mware/parser.h
Normal 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
2
bertos/mware/pgm.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#warning This header is OBSOLETE
|
||||
#include <cpu/pgm.h>
|
||||
497
bertos/mware/readline.c
Normal file
497
bertos/mware/readline.c
Normal 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
120
bertos/mware/readline.h
Normal 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
113
bertos/mware/resource.c
Normal 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
105
bertos/mware/resource.h
Normal 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
2
bertos/mware/rle.h
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#warning This header is OBSOLETE
|
||||
#include <algo/rle.h>
|
||||
157
bertos/mware/sprintf.c
Normal file
157
bertos/mware/sprintf.c
Normal 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
114
bertos/mware/sprintf_test.c
Normal 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
99
bertos/mware/strtol10.c
Normal 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
65
bertos/mware/strtol10.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue