Skip to content

Commit

Permalink
fwk: Introduce input/output component
Browse files Browse the repository at this point in the history
This change introduces the framework input/output (I/O) component, which
enables low-level read and write operations to arbitrary entities. This
component is available before the module runtime is available, and can
therefore be used for debugging module initialization routines and
console input/output (assuming an entity is available to handle it).

For documentation on the I/O component, see `<fwk_io.h>`.

This supplants much of the existing logging implementation, including
the `log` module, though the public logging component interface remains
largely the same. The "always-on backend"/"dynamic backend" concept has
been removed as the logging component now uses the I/O component under
the hood. See

For documentation on the Log component, see `<fwk_log.h>`.

Change-Id: I9cfcbe63220969b5a2f47333a89c8d1b287589ac
Signed-off-by: Chris Kay <[email protected]>
  • Loading branch information
CJKay authored and jimqui01 committed Sep 8, 2020
1 parent a2ab988 commit 77f68b5
Show file tree
Hide file tree
Showing 95 changed files with 1,983 additions and 1,421 deletions.
526 changes: 526 additions & 0 deletions framework/include/fwk_io.h

Large diffs are not rendered by default.

192 changes: 46 additions & 146 deletions framework/include/fwk_log.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
#define FWK_LOG_H

#include <fwk_attributes.h>
#include <fwk_io.h>
#include <fwk_macros.h>

#include <stdbool.h>

#if FWK_HAS_INCLUDE(<fmw_log.h>)
# include <fmw_log.h>
#endif
Expand All @@ -26,8 +25,8 @@
* \addtogroup GroupLogging Logging
*
* \details This framework component provides logging facilities to the
* firmware. It is intended to be simple and flexible to enable rapid
* prototyping of debug-quality code, and performant release-quality code.
* firmware. It is intended to be simple and flexible to enable robust
* string logging for user consumption.
*
* This component provides five filter levels for logging messages.
* Log messages are assigned a filter level based on the logging macro
Expand All @@ -46,50 +45,37 @@
* build system configuration options, determines the minimum level a log
* message must be for it to be included in the binary.
*
* The logging framework component supports two different backends: the
* always-on backend, which provides basic logging facilities for very
* early logging, and the dynamic backend, which is intended to
* supplant the always-on backend at runtime to provide a more flexible
* implementation. These backends are registered through
* ::fwk_log_register_aon() and ::fwk_log_register().
*
* An always-on backend can be registered by any firmware at a very early
* stage of the boot-up process, including before initialization of
* individual modules. This backend is intended to provide a bridge to a
* simple always-on device, such as a UART, that can be used as an output
* for log messages before a dynamic backend has been registered. See the
* documentation for ::fwk_log_register_aon() for more information.
*
* A dynamic backend should be registered by a module, and is expected to
* handle more complex scenarios more robustly. This backend may choose to
* support multiple log devices of its own, or may support devices that are
* not always available. A reference implementation is provided as part of
* the standard logging module.
*
* If buffering has been enabled (via ::FMW_LOG_BUFFER_SIZE) then log
* messages may be buffered to reduce response latency; these buffered log
* messages will be flushed to the dynamic backend once the system has
* reached an idle state.
* If buffering has been enabled then log messages may be buffered to
* reduce overall firmware response latency; these buffered log messages
* will be flushed once the system has reached an idle state. By default,
* buffering is disabled in debug mode and enabled for all platforms in
* release mode, but this behaviour can be adjusted by configuring
* ::FMW_LOG_BUFFER_SIZE.
*
* If buffering is enabled, log messages will be buffered if:
* The device used for logging can also be adjusted through
* ::FMW_LOG_DRAIN_ID. The default behaviour resorts to using the entity
* described by ::FMW_IO_STDOUT_ID as the logging device.
*
* - A dynamic backend is currently registered, or
* - No backends are currently registered
* If a message is too large to fit into the remaining space of the
* internal buffer, the message will be dropped.
*
* Buffered log messages will only ever be flushed to the dynamic backend.
*
* Log messages will be written directly to the always-on backend if:
*
* - An always-on backend is currently registered, and
* - No dynamic backend is currently registered
* Note that log messages are terminated at the column dictated by
* ::FMW_LOG_COLUMNS, or the earliest newline.
* \{
*/

/*!
* \def FMW_LOG_DRAIN_ID
*
* If buffering is disabled, log messages will be written directly to the
* dynamic backend if one is registered, otherwise to the always-on backend
* if one is registered.
* \brief Identifier of the log drain.
*
* In any other case, log messages will be dropped.
* \{
* \details The log drain represents an entity to which logging messages will
* be written, and defaults to ::FMW_IO_STDOUT_ID. Replacing the log drain
* identifier allows log messages and normal input/output to be separated.
*/
#ifndef FMW_LOG_DRAIN_ID
# define FMW_LOG_DRAIN_ID FMW_IO_STDOUT_ID
#endif

/*!
* \def FMW_LOG_BUFFER_SIZE
Expand Down Expand Up @@ -246,7 +232,7 @@
*/

#if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_TRACE
# define FWK_LOG_TRACE(...) fwk_log_snprintf(true, __VA_ARGS__)
# define FWK_LOG_TRACE(...) fwk_log_printf(__VA_ARGS__)
#else
# define FWK_LOG_TRACE(...) FWK_LOG_VOID(__VA_ARGS__)
#endif
Expand All @@ -260,7 +246,7 @@
*/

#if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_INFO
# define FWK_LOG_INFO(...) fwk_log_snprintf(true, __VA_ARGS__)
# define FWK_LOG_INFO(...) fwk_log_printf(__VA_ARGS__)
#else
# define FWK_LOG_INFO(...) FWK_LOG_VOID(__VA_ARGS__)
#endif
Expand All @@ -274,7 +260,7 @@
*/

#if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_WARN
# define FWK_LOG_WARN(...) fwk_log_snprintf(true, __VA_ARGS__)
# define FWK_LOG_WARN(...) fwk_log_printf(__VA_ARGS__)
#else
# define FWK_LOG_WARN(...) FWK_LOG_VOID(__VA_ARGS__)
#endif
Expand All @@ -288,7 +274,7 @@
*/

#if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_ERROR
# define FWK_LOG_ERR(...) fwk_log_snprintf(true, __VA_ARGS__)
# define FWK_LOG_ERR(...) fwk_log_printf(__VA_ARGS__)
#else
# define FWK_LOG_ERR(...) FWK_LOG_VOID(__VA_ARGS__)
#endif
Expand All @@ -302,118 +288,20 @@
*/

#if FWK_LOG_LEVEL <= FWK_LOG_LEVEL_CRIT
# define FWK_LOG_CRIT(...) fwk_log_snprintf(true, __VA_ARGS__)
# define FWK_LOG_CRIT(...) fwk_log_printf(__VA_ARGS__)
#else
# define FWK_LOG_CRIT(...) FWK_LOG_VOID(__VA_ARGS__)
#endif

/*!
* \brief Logging backend interface.
*
* \details This interface represents the medium through which the framework
* logging logic transmits data to one or more logging backends.
*/
struct fwk_log_backend {
/*!
* \brief Buffer a single character.
*
* \note If the backend is not buffered, it may instead choose to print
* the character immediately.
*
* \param[in] ch Character to buffer.
*
* \retval ::FWK_E_DEVICE The operation failed.
* \retval ::FWK_SUCCESS The operation succeeded.
*
* \return Status code representing the result of the operation.
*/
int (*print)(char ch);

/*!
* \brief Flush the backend's buffer (if one exists).
*
* \note May be set to \c NULL if the backend is not buffered.
*
* \param[in] backend Logging backend.
*
* \retval ::FWK_E_DEVICE The operation failed.
* \retval ::FWK_SUCCESS The operation succeeded.
*
* \return Status code representing the result of the operation.
*/
int (*flush)(void);
};

/*!
* \brief Register an always-on backend.
*
* \details For very early printf-style debugging, an always-on backend may be
* registered from within a [constructor function].
*
* [constructor function]:
* https://developer.arm.com/docs/100067/latest/compiler-specific-function-variable-and-type-attributes/__attribute__constructorpriority-function-attribute
*
* \param[in] backend Logging backend to register.
*
* \retval ::FWK_E_PARAM One or more parameters were invalid.
* \retval ::FWK_SUCCESS The operation succeeded.
*
* \return Status code representing the result of the operation.
*/
int fwk_log_register_aon(const struct fwk_log_backend *backend);

/*!
* \brief Deregister the current always-on backend.
*
* \details This step is recommended if your logging backend can no longer
* supply the facilities required.
*
* \return The previously registered backend, or \c NULL if no backend was
* registered.
*/
const struct fwk_log_backend *fwk_log_deregister_aon(void);

/*!
* \brief Register a dynamic backend.
*
* \details The dynamic backend is intended to be the primary runtime backend,
* and can be used to handle more complex devices such as a UART behind a
* power domain.
*
* \note If buffering is enabled and a dynamic backend has been
* registered, log messages will be buffered.
*
* \param[in] backend Logging backend to register.
*
* \retval ::FWK_E_PARAM One or more parameters were invalid.
* \retval ::FWK_SUCCESS The operation succeeded.
*
* \return Status code representing the result of the operation.
*/
int fwk_log_register(const struct fwk_log_backend *backend);

/*!
* \brief Deregister the current dynamic backend.
*
* \details This step is recommended if your logging backend can no longer
* supply the facilities required.
*
* \return The previously registered backend, or \c NULL if no backend was
* registered.
*/
const struct fwk_log_backend *fwk_log_deregister(void);

/*!
* \internal
*
* \brief Log a message with a specified filter level.
*
* \param[in] print_banner Try to print the initialization banner.
* \param[in] format Format string.
* \param[in] ... Associated parameters.
*/
void fwk_log_snprintf(bool print_banner, const char *format, ...)
FWK_PRINTF(2, 3);
void fwk_log_printf(const char *format, ...) FWK_PRINTF(1, 2);

/*!
* \internal
Expand Down Expand Up @@ -441,6 +329,18 @@ void fwk_log_flush(void);
*/
int fwk_log_unbuffer(void);

/*!
* \internal
*
* \brief Initialize the logging component.
*
* \details Initializes the logging framework component, making logging
* facilities to the framework and, later, modules.
*
* \return Status code representing the result of the operation.
*/
int fwk_log_init(void);

/*!
* \}
*/
Expand Down
33 changes: 33 additions & 0 deletions framework/include/fwk_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <fwk_element.h>
#include <fwk_event.h>
#include <fwk_id.h>
#include <fwk_io.h>

#include <stdbool.h>
#include <stdint.h>
Expand Down Expand Up @@ -88,6 +89,19 @@ struct fwk_module {
unsigned int notification_count;
#endif

/*!
* \brief Stream adapter.
*
* \details Every module may provide an optional stream adapter, which
* allows it to service input/output requests to and from other modules
* in a fashion similar to standard file operations in the standard
* library.
*
* This adapter handles adapter requests to the module, its elements,
* and its sub-elements.
*/
struct fwk_io_adapter adapter;

/*!
* \brief Pointer to the module initialization function.
*
Expand Down Expand Up @@ -536,6 +550,25 @@ const void *fwk_module_get_data(fwk_id_t id);
*/
int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api);

/*!
* \brief Get the [stream adapter](::fwk_module::adapter) of a module.
*
* \details Stream adapters are owned by the module, rather than its elements or
* sub-elements, but element and sub-element identifiers may also be
* provided to this function to get the adapter of the parent module.
*
* \param[out] adapter Pointer to the stream adapter belonging to the entity, or
* a null pointer value if the module has no registered adapter.
* \param[in] id Identifier of an entity.
*
* \return Status code representing the result of the operation.
*
* \retval ::FWK_SUCCESS The operation succeeded.
* \retval ::FWK_E_PARAM The `adapter` parameter was a null pointer value.
* \retval ::FWK_E_PARAM The `id` parameter did not resolve to a valid entity.
*/
int fwk_module_adapter(const struct fwk_io_adapter **adapter, fwk_id_t id);

/*!
* \internal
*
Expand Down
1 change: 1 addition & 0 deletions framework/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ BS_LIB_SOURCES += fwk_arch.c
BS_LIB_SOURCES += fwk_dlist.c
BS_LIB_SOURCES += fwk_id.c
BS_LIB_SOURCES += fwk_interrupt.c
BS_LIB_SOURCES += fwk_io.c
BS_LIB_SOURCES += fwk_log.c
BS_LIB_SOURCES += fwk_mm.c
BS_LIB_SOURCES += fwk_module.c
Expand Down
13 changes: 13 additions & 0 deletions framework/src/fwk_arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@

#include <fwk_arch.h>
#include <fwk_assert.h>
#include <fwk_io.h>
#include <fwk_log.h>
#include <fwk_module.h>
#include <fwk_module_idx.h>
#include <fwk_status.h>

#include <string.h>

extern int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *driver);

static int fwk_arch_interrupt_init(int (*interrupt_init_handler)(
Expand Down Expand Up @@ -51,6 +56,14 @@ int fwk_arch_init(const struct fwk_arch_init_driver *driver)

fwk_module_init();

status = fwk_io_init();
if (!fwk_expect(status == FWK_SUCCESS))
return FWK_E_PANIC;

status = fwk_log_init();
if (!fwk_expect(status == FWK_SUCCESS))
return FWK_E_PANIC;

/* Initialize interrupt management */
status = fwk_arch_interrupt_init(driver->interrupt);
if (!fwk_expect(status == FWK_SUCCESS))
Expand Down
Loading

0 comments on commit 77f68b5

Please sign in to comment.