bsps: Move generic IRQ support to bsps

This patch is a part of the BSP source reorganization.

Update #3285.
This commit is contained in:
Sebastian Huber
2018-04-05 06:40:02 +02:00
parent d5842691e1
commit 9b7c456732
51 changed files with 96 additions and 240 deletions

View File

@@ -0,0 +1,5 @@
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-generic.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-info.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-legacy.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-server.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-shell.c

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2008-2012 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Obere Lagerstr. 30
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <inttypes.h>
#include <rtems/bspIo.h>
#include <bsp/irq-generic.h>
void bsp_interrupt_handler_default(rtems_vector_number vector)
{
printk("spurious interrupt: %" PRIu32 "\n", vector);
}

View File

@@ -0,0 +1,627 @@
/**
* @file
*
* @ingroup bsp_interrupt
*
* @brief Generic BSP interrupt support implementation.
*/
/*
* Based on concepts of Pavel Pisa, Till Straumann and Eric Valette.
*
* Copyright (c) 2008, 2017 embedded brains GmbH.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <bsp/irq-generic.h>
#include <bsp/fatal.h>
#include <stdlib.h>
#include <rtems/score/apimutex.h>
#include <rtems/score/processormask.h>
#include <rtems/score/sysstate.h>
#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
bsp_interrupt_handler_index_type bsp_interrupt_handler_index_table
[BSP_INTERRUPT_VECTOR_NUMBER];
#endif
bsp_interrupt_handler_entry bsp_interrupt_handler_table
[BSP_INTERRUPT_HANDLER_TABLE_SIZE];
/* The last entry indicates if everything is initialized */
static uint8_t bsp_interrupt_handler_unique_table
[(BSP_INTERRUPT_HANDLER_TABLE_SIZE + 7 + 1) / 8];
static void bsp_interrupt_handler_empty(void *arg)
{
rtems_vector_number vector = (rtems_vector_number) (uintptr_t) arg;
bsp_interrupt_handler_default(vector);
}
#ifdef RTEMS_SMP
static void bsp_interrupt_handler_do_nothing(void *arg)
{
(void) arg;
}
#endif
static inline bool bsp_interrupt_is_handler_unique(rtems_vector_number index)
{
rtems_vector_number i = index / 8;
rtems_vector_number s = index % 8;
return (bsp_interrupt_handler_unique_table [i] >> s) & 0x1;
}
static inline void bsp_interrupt_set_handler_unique(
rtems_vector_number index,
bool unique
)
{
rtems_vector_number i = index / 8;
rtems_vector_number s = index % 8;
if (unique) {
bsp_interrupt_handler_unique_table [i] |= (uint8_t) (0x1U << s);
} else {
bsp_interrupt_handler_unique_table [i] &= (uint8_t) ~(0x1U << s);
}
}
static inline bool bsp_interrupt_is_initialized(void)
{
return bsp_interrupt_is_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE);
}
static inline void bsp_interrupt_set_initialized(void)
{
bsp_interrupt_set_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE, true);
}
static inline bool bsp_interrupt_is_empty_handler_entry(
const bsp_interrupt_handler_entry *e
)
{
return e->handler == bsp_interrupt_handler_empty;
}
static inline void bsp_interrupt_clear_handler_entry(
bsp_interrupt_handler_entry *e,
rtems_vector_number vector
)
{
e->handler = bsp_interrupt_handler_empty;
bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
e->arg = (void *) (uintptr_t) vector;
e->info = NULL;
e->next = NULL;
}
static inline bool bsp_interrupt_allocate_handler_index(
rtems_vector_number vector,
rtems_vector_number *index
)
{
#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
rtems_vector_number i = 0;
/* The first entry will remain empty */
for (i = 1; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
const bsp_interrupt_handler_entry *e = &bsp_interrupt_handler_table [i];
if (bsp_interrupt_is_empty_handler_entry(e)) {
*index = i;
return true;
}
}
return false;
#else
*index = bsp_interrupt_handler_index(vector);
return true;
#endif
}
static bsp_interrupt_handler_entry *bsp_interrupt_allocate_handler_entry(void)
{
#ifdef BSP_INTERRUPT_NO_HEAP_USAGE
rtems_vector_number index = 0;
if (bsp_interrupt_allocate_handler_index(0, &index)) {
return &bsp_interrupt_handler_table [index];
} else {
return NULL;
}
#else
return malloc(sizeof(bsp_interrupt_handler_entry));
#endif
}
static void bsp_interrupt_free_handler_entry(bsp_interrupt_handler_entry *e)
{
#ifdef BSP_INTERRUPT_NO_HEAP_USAGE
bsp_interrupt_clear_handler_entry(e, 0);
#else
free(e);
#endif
}
void bsp_interrupt_lock(void)
{
if (_System_state_Is_up(_System_state_Get())) {
_RTEMS_Lock_allocator();
}
}
void bsp_interrupt_unlock(void)
{
if (_System_state_Is_up(_System_state_Get())) {
_RTEMS_Unlock_allocator();
}
}
void bsp_interrupt_initialize(void)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
size_t i = 0;
/* Initialize handler table */
for (i = 0; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
bsp_interrupt_handler_table [i].handler = bsp_interrupt_handler_empty;
bsp_interrupt_handler_table [i].arg = (void *) i;
}
sc = bsp_interrupt_facility_initialize();
if (sc != RTEMS_SUCCESSFUL) {
bsp_fatal(BSP_FATAL_INTERRUPT_INITIALIZATION);
}
bsp_interrupt_set_initialized();
}
/**
* @brief Installs an interrupt handler.
*
* @ingroup bsp_interrupt
*
* @return In addition to the standard status codes this function returns:
* - If the BSP interrupt support is not initialized RTEMS_INTERNAL_ERROR will
* be returned.
* - If not enough memory for a new handler is available RTEMS_NO_MEMORY will
* be returned
*
* @see rtems_interrupt_handler_install()
*/
static rtems_status_code bsp_interrupt_handler_install(
rtems_vector_number vector,
const char *info,
rtems_option options,
rtems_interrupt_handler handler,
void *arg
)
{
rtems_interrupt_level level;
rtems_vector_number index = 0;
bsp_interrupt_handler_entry *head = NULL;
bool enable_vector = false;
bool replace = RTEMS_INTERRUPT_IS_REPLACE(options);
/* Check parameters and system state */
if (!bsp_interrupt_is_initialized()) {
return RTEMS_INTERNAL_ERROR;
} else if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
} else if (handler == NULL) {
return RTEMS_INVALID_ADDRESS;
} else if (rtems_interrupt_is_in_progress()) {
return RTEMS_CALLED_FROM_ISR;
}
/* Lock */
bsp_interrupt_lock();
/* Get handler table index */
index = bsp_interrupt_handler_index(vector);
/* Get head entry of the handler list for current vector */
head = &bsp_interrupt_handler_table [index];
if (bsp_interrupt_is_empty_handler_entry(head)) {
if (replace) {
/* No handler to replace exists */
bsp_interrupt_unlock();
return RTEMS_UNSATISFIED;
}
/*
* No real handler installed yet. So allocate a new index in
* the handler table and fill the entry with life.
*/
if (bsp_interrupt_allocate_handler_index(vector, &index)) {
bsp_interrupt_disable(level);
bsp_interrupt_handler_table [index].arg = arg;
bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
bsp_interrupt_handler_table [index].handler = handler;
#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
bsp_interrupt_handler_index_table [vector] = index;
#endif
bsp_interrupt_enable(level);
bsp_interrupt_handler_table [index].info = info;
} else {
/* Handler table is full */
bsp_interrupt_unlock();
return RTEMS_NO_MEMORY;
}
/* This is the first handler so enable the vector later */
enable_vector = true;
} else {
bsp_interrupt_handler_entry *current = head;
bsp_interrupt_handler_entry *tail = NULL;
bsp_interrupt_handler_entry *match = NULL;
/* Ensure that a unique handler remains unique */
if (
!replace
&& (RTEMS_INTERRUPT_IS_UNIQUE(options)
|| bsp_interrupt_is_handler_unique(index))
) {
/*
* Tried to install a unique handler on a not empty
* list or there is already a unique handler installed.
*/
bsp_interrupt_unlock();
return RTEMS_RESOURCE_IN_USE;
}
/*
* Search for the list tail and check if the handler is already
* installed.
*/
do {
if (
match == NULL
&& (current->handler == handler || replace)
&& current->arg == arg
) {
match = current;
}
tail = current;
current = current->next;
} while (current != NULL);
if (replace) {
/* Ensure that a handler to replace exists */
if (match == NULL) {
bsp_interrupt_unlock();
return RTEMS_UNSATISFIED;
}
/* Use existing entry */
current = match;
} else {
/* Ensure the handler is not already installed */
if (match != NULL) {
/* The handler is already installed */
bsp_interrupt_unlock();
return RTEMS_TOO_MANY;
}
/* Allocate a new entry */
current = bsp_interrupt_allocate_handler_entry();
if (current == NULL) {
/* Not enough memory */
bsp_interrupt_unlock();
return RTEMS_NO_MEMORY;
}
}
/* Update existing entry or set new entry */
current->handler = handler;
current->info = info;
if (!replace) {
/* Set new entry */
current->arg = arg;
current->next = NULL;
/* Link to list tail */
bsp_interrupt_disable(level);
bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
tail->next = current;
bsp_interrupt_enable(level);
}
}
/* Make the handler unique if necessary */
bsp_interrupt_set_handler_unique(index, RTEMS_INTERRUPT_IS_UNIQUE(options));
/* Enable the vector if necessary */
if (enable_vector) {
bsp_interrupt_vector_enable(vector);
}
/* Unlock */
bsp_interrupt_unlock();
return RTEMS_SUCCESSFUL;
}
/**
* @brief Removes an interrupt handler.
*
* @ingroup bsp_interrupt
*
* @return In addition to the standard status codes this function returns
* RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
*
* @see rtems_interrupt_handler_remove().
*/
static rtems_status_code bsp_interrupt_handler_remove(
rtems_vector_number vector,
rtems_interrupt_handler handler,
void *arg
)
{
rtems_interrupt_level level;
rtems_vector_number index = 0;
bsp_interrupt_handler_entry *head = NULL;
bsp_interrupt_handler_entry *current = NULL;
bsp_interrupt_handler_entry *previous = NULL;
bsp_interrupt_handler_entry *match = NULL;
/* Check parameters and system state */
if (!bsp_interrupt_is_initialized()) {
return RTEMS_INTERNAL_ERROR;
} else if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
} else if (handler == NULL) {
return RTEMS_INVALID_ADDRESS;
} else if (rtems_interrupt_is_in_progress()) {
return RTEMS_CALLED_FROM_ISR;
}
/* Lock */
bsp_interrupt_lock();
/* Get handler table index */
index = bsp_interrupt_handler_index(vector);
/* Get head entry of the handler list for current vector */
head = &bsp_interrupt_handler_table [index];
/* Search for a matching entry */
current = head;
do {
if (current->handler == handler && current->arg == arg) {
match = current;
break;
}
previous = current;
current = current->next;
} while (current != NULL);
/* Remove the matching entry */
if (match != NULL) {
if (match->next != NULL) {
/*
* The match has a successor. A successor is always
* allocated. So replace the match with its successor
* and free the successor entry.
*/
current = match->next;
bsp_interrupt_disable(level);
#ifdef RTEMS_SMP
match->handler = bsp_interrupt_handler_do_nothing;
bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
#endif
match->arg = current->arg;
bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
match->handler = current->handler;
match->info = current->info;
match->next = current->next;
bsp_interrupt_enable(level);
bsp_interrupt_free_handler_entry(current);
} else if (match == head) {
/*
* The match is the list head and has no successor.
* The list head is stored in a static table so clear
* this entry. Since now the list is empty disable the
* vector.
*/
/* Disable the vector */
bsp_interrupt_vector_disable(vector);
/* Clear entry */
bsp_interrupt_disable(level);
bsp_interrupt_clear_handler_entry(head, vector);
#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
bsp_interrupt_handler_index_table [vector] = 0;
#endif
bsp_interrupt_enable(level);
/* Allow shared handlers */
bsp_interrupt_set_handler_unique(index, false);
} else {
/*
* The match is the list tail and has a predecessor.
* So terminate the predecessor and free the match.
*/
bsp_interrupt_disable(level);
previous->next = NULL;
bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
bsp_interrupt_enable(level);
bsp_interrupt_free_handler_entry(match);
}
} else {
/* No matching entry found */
bsp_interrupt_unlock();
return RTEMS_UNSATISFIED;
}
/* Unlock */
bsp_interrupt_unlock();
return RTEMS_SUCCESSFUL;
}
/**
* @brief Iterates over all installed interrupt handler of a vector.
*
* @ingroup bsp_interrupt
*
* @return In addition to the standard status codes this function returns
* RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
*
* @see rtems_interrupt_handler_iterate().
*/
static rtems_status_code bsp_interrupt_handler_iterate(
rtems_vector_number vector,
rtems_interrupt_per_handler_routine routine,
void *arg
)
{
bsp_interrupt_handler_entry *current = NULL;
rtems_option options = 0;
rtems_vector_number index = 0;
/* Check parameters and system state */
if (!bsp_interrupt_is_initialized()) {
return RTEMS_INTERNAL_ERROR;
} else if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
} else if (rtems_interrupt_is_in_progress()) {
return RTEMS_CALLED_FROM_ISR;
}
/* Lock */
bsp_interrupt_lock();
/* Interate */
index = bsp_interrupt_handler_index(vector);
current = &bsp_interrupt_handler_table [index];
if (!bsp_interrupt_is_empty_handler_entry(current)) {
do {
options = bsp_interrupt_is_handler_unique(index) ?
RTEMS_INTERRUPT_UNIQUE : RTEMS_INTERRUPT_SHARED;
routine(arg, current->info, options, current->handler, current->arg);
current = current->next;
} while (current != NULL);
}
/* Unlock */
bsp_interrupt_unlock();
return RTEMS_SUCCESSFUL;
}
rtems_status_code rtems_interrupt_handler_install(
rtems_vector_number vector,
const char *info,
rtems_option options,
rtems_interrupt_handler handler,
void *arg
)
{
return bsp_interrupt_handler_install(vector, info, options, handler, arg);
}
rtems_status_code rtems_interrupt_handler_remove(
rtems_vector_number vector,
rtems_interrupt_handler handler,
void *arg
)
{
return bsp_interrupt_handler_remove(vector, handler, arg);
}
rtems_status_code rtems_interrupt_handler_iterate(
rtems_vector_number vector,
rtems_interrupt_per_handler_routine routine,
void *arg
)
{
return bsp_interrupt_handler_iterate(vector, routine, arg);
}
bool bsp_interrupt_handler_is_empty(rtems_vector_number vector)
{
rtems_vector_number index = 0;
bsp_interrupt_handler_entry *head = NULL;
bool empty;
/* For use in interrupts so no lock. */
/* Get handler table index */
index = bsp_interrupt_handler_index(vector);
/* Get head entry of the handler list for the vector */
head = &bsp_interrupt_handler_table [index];
empty = bsp_interrupt_is_empty_handler_entry(head);
return empty;
}
rtems_status_code rtems_interrupt_set_affinity(
rtems_vector_number vector,
size_t affinity_size,
const cpu_set_t *affinity
)
{
Processor_mask set;
Processor_mask_Copy_status status;
if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
}
status = _Processor_mask_From_cpu_set_t(&set, affinity_size, affinity);
if (status != PROCESSOR_MASK_COPY_LOSSLESS) {
return RTEMS_INVALID_SIZE;
}
#if defined(RTEMS_SMP)
bsp_interrupt_set_affinity(vector, &set);
#endif
return RTEMS_SUCCESSFUL;
}
rtems_status_code rtems_interrupt_get_affinity(
rtems_vector_number vector,
size_t affinity_size,
cpu_set_t *affinity
)
{
Processor_mask set;
Processor_mask_Copy_status status;
if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
}
#if defined(RTEMS_SMP)
bsp_interrupt_get_affinity(vector, &set);
#else
_Processor_mask_From_index(&set, 0);
#endif
status = _Processor_mask_To_cpu_set_t(&set, affinity_size, affinity);
if (status != PROCESSOR_MASK_COPY_LOSSLESS) {
return RTEMS_INVALID_SIZE;
}
return RTEMS_SUCCESSFUL;
}

View File

@@ -0,0 +1,95 @@
/**
* @file
*
* @ingroup bsp_interrupt
*
* @brief Generic BSP interrupt information implementation.
*/
/*
* Copyright (c) 2008, 2009, 2010
* embedded brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <inttypes.h>
#include <rtems/printer.h>
#include <bsp/irq-generic.h>
#include <bsp/irq-info.h>
typedef struct {
const rtems_printer *printer;
rtems_vector_number vector;
} bsp_interrupt_report_entry;
static void bsp_interrupt_report_per_handler_routine(
void *arg,
const char *info,
rtems_option options,
rtems_interrupt_handler handler,
void *handler_arg
)
{
bsp_interrupt_report_entry *e = (bsp_interrupt_report_entry *) arg;
const char *opt = options == RTEMS_INTERRUPT_UNIQUE ? "UNIQUE" : "SHARED";
rtems_printf(
e->printer,
"%7" PRIu32 " | %-32s | %7s | %p | %p\n",
e->vector,
info,
opt,
handler,
handler_arg
);
}
void bsp_interrupt_report_with_plugin(
const rtems_printer *printer
)
{
rtems_vector_number v = 0;
bsp_interrupt_report_entry e = {
.printer = printer,
.vector = 0
};
rtems_printf(
printer,
"-------------------------------------------------------------------------------\n"
" INTERRUPT INFORMATION\n"
"--------+----------------------------------+---------+------------+------------\n"
" VECTOR | INFO | OPTIONS | HANDLER | ARGUMENT \n"
"--------+----------------------------------+---------+------------+------------\n"
);
for (v = BSP_INTERRUPT_VECTOR_MIN; v <= BSP_INTERRUPT_VECTOR_MAX; ++v) {
e.vector = v;
rtems_interrupt_handler_iterate(
v,
bsp_interrupt_report_per_handler_routine,
&e
);
}
rtems_printf(
printer,
"--------+----------------------------------+---------+------------+------------\n"
);
}
void bsp_interrupt_report(void)
{
rtems_printer printer;
rtems_print_printer_printk(&printer);
bsp_interrupt_report_with_plugin(&printer);
}

View File

@@ -0,0 +1,125 @@
/**
* @file
*
* @ingroup bsp_interrupt
*
* @brief Generic BSP interrupt support legacy implementation.
*/
/*
* Copyright (c) 2008, 2009
* embedded brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <string.h>
#define BSP_SHARED_HANDLER_SUPPORT
#include <rtems.h>
#include <rtems/irq.h>
#include <bsp/irq-generic.h>
/**
* @deprecated Obsolete.
*/
int BSP_get_current_rtems_irq_handler(rtems_irq_connect_data *cd)
{
memset(cd, 0, sizeof(*cd));
return 1;
}
/**
* @deprecated Use rtems_interrupt_handler_install() instead.
*/
int BSP_install_rtems_irq_handler(const rtems_irq_connect_data *cd)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
sc = rtems_interrupt_handler_install(
cd->name,
"LEGACY INSTALLED",
RTEMS_INTERRUPT_UNIQUE,
cd->hdl,
cd->handle
);
if (sc != RTEMS_SUCCESSFUL) {
return 0;
}
if (cd->on != NULL) {
cd->on(cd);
}
return 1;
}
/**
* @deprecated Use rtems_interrupt_handler_install() instead.
*/
int BSP_install_rtems_shared_irq_handler(const rtems_irq_connect_data *cd)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
sc = rtems_interrupt_handler_install(
cd->name,
"LEGACY INSTALLED",
RTEMS_INTERRUPT_SHARED,
cd->hdl,
cd->handle
);
if (sc != RTEMS_SUCCESSFUL) {
return 0;
}
if (cd->on != NULL) {
(*cd->on)(cd);
}
return 1;
}
/**
* @deprecated Use rtems_interrupt_handler_remove() instead.
*/
int BSP_remove_rtems_irq_handler(const rtems_irq_connect_data *cd)
{
rtems_status_code sc = RTEMS_SUCCESSFUL;
if (cd->off != NULL) {
(*cd->off)(cd);
}
sc = rtems_interrupt_handler_remove(cd->name, cd->hdl, cd->handle);
if (sc != RTEMS_SUCCESSFUL) {
return 0;
}
return 1;
}
/**
* @deprecated Use bsp_interrupt_initialize() instead.
*/
int BSP_rtems_irq_mngt_set(rtems_irq_global_settings *config)
{
return 0;
}
/**
* @deprecated Obsolete.
*/
int BSP_rtems_irq_mngt_get(rtems_irq_global_settings **config)
{
*config = NULL;
return 0;
}

View File

@@ -0,0 +1,863 @@
/**
* @file
*
* @ingroup bsp_interrupt
*
* @brief Generic BSP interrupt server implementation.
*/
/*
* Copyright (c) 2009, 2017 embedded brains GmbH. All rights reserved.
*
* embedded brains GmbH
* Dornierstr. 4
* 82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <stdlib.h>
#include <rtems.h>
#include <rtems/chain.h>
#include <rtems/score/assert.h>
#include <bsp/irq-generic.h>
#define BSP_INTERRUPT_SERVER_MANAGEMENT_VECTOR (BSP_INTERRUPT_VECTOR_MAX + 1)
typedef struct {
RTEMS_INTERRUPT_LOCK_MEMBER(lock);
rtems_chain_control entries;
rtems_id server;
unsigned errors;
} bsp_interrupt_server_context;
#if defined(RTEMS_SMP)
static bsp_interrupt_server_context *bsp_interrupt_server_instances;
#else
static bsp_interrupt_server_context bsp_interrupt_server_instance;
#endif
static bsp_interrupt_server_context *bsp_interrupt_server_get_context(
uint32_t server_index,
rtems_status_code *sc
)
{
#if defined(RTEMS_SMP)
if (bsp_interrupt_server_instances == NULL) {
*sc = RTEMS_INCORRECT_STATE;
return NULL;
}
#else
if (bsp_interrupt_server_instance.server == RTEMS_ID_NONE) {
*sc = RTEMS_INCORRECT_STATE;
return NULL;
}
#endif
if (server_index >= rtems_get_processor_count()) {
*sc = RTEMS_INVALID_ID;
return NULL;
}
*sc = RTEMS_SUCCESSFUL;
#if defined(RTEMS_SMP)
return &bsp_interrupt_server_instances[server_index];
#else
return &bsp_interrupt_server_instance;
#endif
}
static void bsp_interrupt_server_trigger(void *arg)
{
rtems_interrupt_lock_context lock_context;
rtems_interrupt_server_entry *e = arg;
bsp_interrupt_server_context *s = e->server;
if (bsp_interrupt_is_valid_vector(e->vector)) {
bsp_interrupt_vector_disable(e->vector);
}
rtems_interrupt_lock_acquire(&s->lock, &lock_context);
if (rtems_chain_is_node_off_chain(&e->node)) {
rtems_chain_append_unprotected(&s->entries, &e->node);
} else {
++s->errors;
}
rtems_interrupt_lock_release(&s->lock, &lock_context);
rtems_event_system_send(s->server, RTEMS_EVENT_SYSTEM_SERVER);
}
typedef struct {
rtems_interrupt_server_entry *entry;
rtems_option *options;
} bsp_interrupt_server_iterate_entry;
static void bsp_interrupt_server_per_handler_routine(
void *iterate_arg,
const char *info,
rtems_option options,
rtems_interrupt_handler handler,
void *handler_arg
)
{
if (handler == bsp_interrupt_server_trigger) {
bsp_interrupt_server_iterate_entry *ie = iterate_arg;
ie->entry = handler_arg;
*ie->options = options;
}
}
static rtems_interrupt_server_entry *bsp_interrupt_server_query_entry(
rtems_vector_number vector,
rtems_option *trigger_options
)
{
bsp_interrupt_server_iterate_entry ie = {
.entry = NULL,
.options = trigger_options
};
rtems_interrupt_handler_iterate(
vector,
bsp_interrupt_server_per_handler_routine,
&ie
);
return ie.entry;
}
typedef struct {
bsp_interrupt_server_context *server;
rtems_vector_number vector;
rtems_option options;
rtems_interrupt_handler handler;
void *arg;
rtems_id task;
rtems_status_code sc;
} bsp_interrupt_server_helper_data;
static void bsp_interrupt_server_install_helper(void *arg)
{
bsp_interrupt_server_helper_data *hd = arg;
rtems_status_code sc;
rtems_interrupt_server_entry *e;
rtems_interrupt_server_action *a;
rtems_option trigger_options;
a = calloc(1, sizeof(*a));
if (a == NULL) {
hd->sc = RTEMS_NO_MEMORY;
rtems_event_transient_send(hd->task);
return;
}
a->handler = hd->handler;
a->arg = hd->arg;
bsp_interrupt_lock();
e = bsp_interrupt_server_query_entry(hd->vector, &trigger_options);
if (e == NULL) {
e = calloc(1, sizeof(*e));
if (e != NULL) {
e->server = hd->server;
e->vector = hd->vector;
e->actions = a;
sc = rtems_interrupt_handler_install(
hd->vector,
"IRQS",
hd->options & RTEMS_INTERRUPT_UNIQUE,
bsp_interrupt_server_trigger,
e
);
if (sc != RTEMS_SUCCESSFUL) {
free(e);
}
} else {
sc = RTEMS_NO_MEMORY;
}
#if defined(RTEMS_SMP)
} else if (e->server != hd->server) {
sc = RTEMS_RESOURCE_IN_USE;
#endif
} else if (
RTEMS_INTERRUPT_IS_UNIQUE(hd->options)
|| RTEMS_INTERRUPT_IS_UNIQUE(trigger_options)
) {
sc = RTEMS_RESOURCE_IN_USE;
} else {
rtems_interrupt_server_action **link = &e->actions;
rtems_interrupt_server_action *c;
sc = RTEMS_SUCCESSFUL;
while ((c = *link) != NULL) {
if (c->handler == hd->handler && c->arg == hd->arg) {
sc = RTEMS_TOO_MANY;
break;
}
link = &c->next;
}
if (sc == RTEMS_SUCCESSFUL) {
*link = a;
}
}
bsp_interrupt_unlock();
if (sc != RTEMS_SUCCESSFUL) {
free(a);
}
hd->sc = sc;
rtems_event_transient_send(hd->task);
}
static void bsp_interrupt_server_remove_helper(void *arg)
{
bsp_interrupt_server_helper_data *hd = arg;
rtems_status_code sc;
rtems_interrupt_server_entry *e;
rtems_option trigger_options;
bsp_interrupt_lock();
e = bsp_interrupt_server_query_entry(hd->vector, &trigger_options);
if (e != NULL) {
rtems_interrupt_server_action **link = &e->actions;
rtems_interrupt_server_action *c;
while ((c = *link) != NULL) {
if (c->handler == hd->handler && c->arg == hd->arg) {
break;
}
link = &c->next;
}
if (c != NULL) {
bool remove_last = e->actions->next == NULL;
if (remove_last) {
rtems_interrupt_handler_remove(
hd->vector,
bsp_interrupt_server_trigger,
e
);
}
*link = c->next;
free(c);
if (remove_last) {
free(e);
}
sc = RTEMS_SUCCESSFUL;
} else {
sc = RTEMS_UNSATISFIED;
}
} else {
sc = RTEMS_INVALID_ID;
}
bsp_interrupt_unlock();
hd->sc = sc;
rtems_event_transient_send(hd->task);
}
static rtems_status_code bsp_interrupt_server_call_helper(
bsp_interrupt_server_context *s,
rtems_vector_number vector,
rtems_option options,
rtems_interrupt_handler handler,
void *arg,
void (*helper)(void *)
)
{
bsp_interrupt_server_helper_data hd = {
.server = s,
.vector = vector,
.options = options,
.handler = handler,
.arg = arg,
.task = rtems_task_self()
};
rtems_interrupt_server_action a = {
.handler = helper,
.arg = &hd
};
rtems_interrupt_server_entry e = {
.server = s,
.vector = BSP_INTERRUPT_SERVER_MANAGEMENT_VECTOR,
.actions = &a
};
bsp_interrupt_server_trigger(&e);
rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
return hd.sc;
}
static rtems_interrupt_server_entry *bsp_interrupt_server_get_entry(
bsp_interrupt_server_context *s
)
{
rtems_interrupt_lock_context lock_context;
rtems_interrupt_server_entry *e;
rtems_interrupt_lock_acquire(&s->lock, &lock_context);
if (!rtems_chain_is_empty(&s->entries)) {
e = (rtems_interrupt_server_entry *)
rtems_chain_get_first_unprotected(&s->entries);
rtems_chain_set_off_chain(&e->node);
} else {
e = NULL;
}
rtems_interrupt_lock_release(&s->lock, &lock_context);
return e;
}
static void bsp_interrupt_server_task(rtems_task_argument arg)
{
bsp_interrupt_server_context *s = (bsp_interrupt_server_context *) arg;
while (true) {
rtems_event_set events;
rtems_interrupt_server_entry *e;
rtems_event_system_receive(
RTEMS_EVENT_SYSTEM_SERVER,
RTEMS_EVENT_ALL | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
while ((e = bsp_interrupt_server_get_entry(s)) != NULL) {
rtems_interrupt_server_action *action = e->actions;
rtems_vector_number vector = e->vector;
do {
rtems_interrupt_server_action *current = action;
action = action->next;
(*current->handler)(current->arg);
} while (action != NULL);
if (bsp_interrupt_is_valid_vector(vector)) {
bsp_interrupt_vector_enable(vector);
}
}
}
}
rtems_status_code rtems_interrupt_server_handler_install(
uint32_t server_index,
rtems_vector_number vector,
const char *info,
rtems_option options,
rtems_interrupt_handler handler,
void *arg
)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
return bsp_interrupt_server_call_helper(
s,
vector,
options,
handler,
arg,
bsp_interrupt_server_install_helper
);
}
rtems_status_code rtems_interrupt_server_handler_remove(
uint32_t server_index,
rtems_vector_number vector,
rtems_interrupt_handler handler,
void *arg
)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
return bsp_interrupt_server_call_helper(
s,
vector,
0,
handler,
arg,
bsp_interrupt_server_remove_helper
);
}
typedef struct {
rtems_interrupt_per_handler_routine routine;
void *arg;
} bsp_interrupt_server_handler_iterate_helper_data;
static void bsp_interrupt_server_handler_iterate_helper(void *arg)
{
bsp_interrupt_server_helper_data *hd = arg;
bsp_interrupt_server_handler_iterate_helper_data *hihd = hd->arg;
rtems_status_code sc;
rtems_interrupt_server_entry *e;
rtems_option trigger_options;
bsp_interrupt_lock();
e = bsp_interrupt_server_query_entry(hd->vector, &trigger_options);
if (e != NULL) {
rtems_interrupt_server_action **link = &e->actions;
rtems_interrupt_server_action *c;
while ((c = *link) != NULL) {
(*hihd->routine)(hihd->arg, NULL, trigger_options, c->handler, c->arg);
link = &c->next;
}
sc = RTEMS_SUCCESSFUL;
} else {
sc = RTEMS_UNSATISFIED;
}
bsp_interrupt_unlock();
hd->sc = sc;
rtems_event_transient_send(hd->task);
}
rtems_status_code rtems_interrupt_server_handler_iterate(
uint32_t server_index,
rtems_vector_number vector,
rtems_interrupt_per_handler_routine routine,
void *arg
)
{
rtems_status_code sc;
bsp_interrupt_server_handler_iterate_helper_data hihd;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
}
hihd.routine = routine;
hihd.arg = arg;
return bsp_interrupt_server_call_helper(
s,
vector,
0,
NULL,
&hihd,
bsp_interrupt_server_handler_iterate_helper
);
}
rtems_status_code rtems_interrupt_server_initialize(
rtems_task_priority priority,
size_t stack_size,
rtems_mode modes,
rtems_attribute attributes,
uint32_t *server_count
)
{
uint32_t cpu_index;
uint32_t cpu_count;
uint32_t dummy;
bsp_interrupt_server_context *instances;
if (server_count == NULL) {
server_count = &dummy;
}
cpu_count = rtems_get_processor_count();
#if defined(RTEMS_SMP)
instances = calloc(cpu_count, sizeof(*instances));
if (instances == NULL) {
return RTEMS_NO_MEMORY;
}
#else
instances = &bsp_interrupt_server_instance;
#endif
for (cpu_index = 0; cpu_index < cpu_count; ++cpu_index) {
bsp_interrupt_server_context *s = &instances[cpu_index];
rtems_status_code sc;
#if defined(RTEMS_SMP)
rtems_id scheduler;
cpu_set_t cpu;
#endif
rtems_interrupt_lock_initialize(&s->lock, "Interrupt Server");
rtems_chain_initialize_empty(&s->entries);
sc = rtems_task_create(
rtems_build_name('I', 'R', 'Q', 'S'),
priority,
stack_size,
modes,
attributes,
&s->server
);
if (sc != RTEMS_SUCCESSFUL) {
*server_count = cpu_index;
#if defined(RTEMS_SMP)
if (cpu_index > 0) {
return RTEMS_SUCCESSFUL;
}
free(instances);
#endif
return RTEMS_TOO_MANY;
}
#if defined(RTEMS_SMP)
sc = rtems_scheduler_ident_by_processor(cpu_index, &scheduler);
_Assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_set_scheduler(s->server, scheduler, priority);
_Assert(sc == RTEMS_SUCCESSFUL);
CPU_ZERO(&cpu);
CPU_SET(cpu_index, &cpu);
sc = rtems_task_set_affinity(s->server, sizeof(cpu), &cpu);
_Assert(sc == RTEMS_SUCCESSFUL);
#endif
sc = rtems_task_start(
s->server,
bsp_interrupt_server_task,
(rtems_task_argument) s
);
_Assert(sc == RTEMS_SUCCESSFUL);
}
#if defined(RTEMS_SMP)
bsp_interrupt_server_instances = instances;
#endif
*server_count = cpu_index;
return RTEMS_SUCCESSFUL;
}
static void bsp_interrupt_server_entry_initialize(
rtems_interrupt_server_entry *entry,
bsp_interrupt_server_context *s
)
{
rtems_chain_set_off_chain(&entry->node);
entry->server = s;
entry->vector = BSP_INTERRUPT_SERVER_MANAGEMENT_VECTOR;
entry->actions = NULL;
}
static void bsp_interrupt_server_action_prepend(
rtems_interrupt_server_entry *entry,
rtems_interrupt_server_action *action,
rtems_interrupt_handler handler,
void *arg
)
{
action->handler = handler;
action->arg = arg;
action->next = entry->actions;
entry->actions = action;
}
rtems_status_code rtems_interrupt_server_entry_initialize(
uint32_t server_index,
rtems_interrupt_server_entry *entry
)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
bsp_interrupt_server_entry_initialize(entry, s);
return RTEMS_SUCCESSFUL;
}
void rtems_interrupt_server_action_prepend(
rtems_interrupt_server_entry *entry,
rtems_interrupt_server_action *action,
rtems_interrupt_handler handler,
void *arg
)
{
bsp_interrupt_server_action_prepend(entry, action, handler, arg);
}
void rtems_interrupt_server_entry_submit(
rtems_interrupt_server_entry *entry
)
{
bsp_interrupt_server_trigger(entry);
}
static void bsp_interrupt_server_entry_synchronize_helper(void *arg)
{
bsp_interrupt_server_helper_data *hd = arg;
rtems_event_transient_send(hd->task);
}
void rtems_interrupt_server_entry_destroy(
rtems_interrupt_server_entry *entry
)
{
bsp_interrupt_server_context *s;
rtems_interrupt_lock_context lock_context;
s = entry->server;
rtems_interrupt_lock_acquire(&s->lock, &lock_context);
if (!rtems_chain_is_node_off_chain(&entry->node)) {
rtems_chain_extract_unprotected(&entry->node);
rtems_chain_set_off_chain(&entry->node);
}
rtems_interrupt_lock_release(&s->lock, &lock_context);
bsp_interrupt_server_call_helper(
s,
BSP_INTERRUPT_SERVER_MANAGEMENT_VECTOR,
0,
NULL,
NULL,
bsp_interrupt_server_entry_synchronize_helper
);
}
rtems_status_code rtems_interrupt_server_request_initialize(
uint32_t server_index,
rtems_interrupt_server_request *request,
rtems_interrupt_handler handler,
void *arg
)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
bsp_interrupt_server_entry_initialize(&request->entry, s);
bsp_interrupt_server_action_prepend(
&request->entry,
&request->action,
handler,
arg
);
return RTEMS_SUCCESSFUL;
}
static void bsp_interrupt_server_handler_move_helper(void *arg)
{
bsp_interrupt_server_helper_data *hd = arg;
bsp_interrupt_server_handler_iterate_helper_data *hihd = hd->arg;
rtems_interrupt_server_entry *e;
rtems_option trigger_options;
bsp_interrupt_lock();
e = bsp_interrupt_server_query_entry(hd->vector, &trigger_options);
if (e != NULL) {
rtems_interrupt_lock_context lock_context;
bsp_interrupt_server_context *src = e->server;
bsp_interrupt_server_context *dst = hihd->arg;
bool pending;
/* The source server is only used in SMP configurations for the lock */
(void) src;
rtems_interrupt_lock_acquire(&src->lock, &lock_context);
pending = !rtems_chain_is_node_off_chain(&e->node);
if (pending) {
rtems_chain_extract_unprotected(&e->node);
rtems_chain_set_off_chain(&e->node);
}
rtems_interrupt_lock_release(&src->lock, &lock_context);
e->server = dst;
if (pending) {
bsp_interrupt_server_trigger(e);
}
}
bsp_interrupt_unlock();
rtems_event_transient_send(hd->task);
}
rtems_status_code rtems_interrupt_server_move(
uint32_t source_server_index,
rtems_vector_number vector,
uint32_t destination_server_index
)
{
rtems_status_code sc;
bsp_interrupt_server_context *src;
bsp_interrupt_server_context *dst;
bsp_interrupt_server_handler_iterate_helper_data hihd;
src = bsp_interrupt_server_get_context(source_server_index, &sc);
if (src == NULL) {
return sc;
}
dst = bsp_interrupt_server_get_context(destination_server_index, &sc);
if (dst == NULL) {
return sc;
}
if (!bsp_interrupt_is_valid_vector(vector)) {
return RTEMS_INVALID_ID;
}
hihd.arg = dst;
bsp_interrupt_server_call_helper(
src,
vector,
0,
NULL,
&hihd,
bsp_interrupt_server_handler_move_helper
);
return RTEMS_SUCCESSFUL;
}
static void bsp_interrupt_server_entry_suspend_helper(void *arg)
{
bsp_interrupt_server_helper_data *hd = arg;
rtems_event_set events;
rtems_event_transient_send(hd->task);
rtems_event_system_receive(
RTEMS_EVENT_SYSTEM_SERVER_RESUME,
RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
}
rtems_status_code rtems_interrupt_server_suspend(uint32_t server_index)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
bsp_interrupt_server_call_helper(
s,
BSP_INTERRUPT_SERVER_MANAGEMENT_VECTOR,
0,
NULL,
NULL,
bsp_interrupt_server_entry_suspend_helper
);
return RTEMS_SUCCESSFUL;
}
rtems_status_code rtems_interrupt_server_resume(uint32_t server_index)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
rtems_event_system_send(s->server, RTEMS_EVENT_SYSTEM_SERVER_RESUME);
bsp_interrupt_server_call_helper(
s,
BSP_INTERRUPT_SERVER_MANAGEMENT_VECTOR,
0,
NULL,
NULL,
bsp_interrupt_server_entry_synchronize_helper
);
return RTEMS_SUCCESSFUL;
}
rtems_status_code rtems_interrupt_server_set_affinity(
uint32_t server_index,
size_t affinity_size,
const cpu_set_t *affinity,
rtems_task_priority priority
)
{
rtems_status_code sc;
bsp_interrupt_server_context *s;
rtems_id scheduler;
s = bsp_interrupt_server_get_context(server_index, &sc);
if (s == NULL) {
return sc;
}
sc = rtems_scheduler_ident_by_processor_set(
affinity_size,
affinity,
&scheduler
);
if (sc != RTEMS_SUCCESSFUL) {
return sc;
}
sc = rtems_task_set_scheduler(s->server, scheduler, priority);
if (sc != RTEMS_SUCCESSFUL) {
return sc;
}
return rtems_task_set_affinity(s->server, affinity_size, affinity);
}

View File

@@ -0,0 +1,45 @@
/**
* @file
*
* @ingroup bsp_interrupt
*
* @brief Generic BSP interrupt shell implementation.
*/
/*
* Copyright (c) 2009
* embedded brains GmbH
* Obere Lagerstr. 30
* D-82178 Puchheim
* Germany
* <rtems@embedded-brains.de>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <stdio.h>
#include <rtems/printer.h>
#include <rtems/shell.h>
#include <bsp/irq-info.h>
static int bsp_interrupt_shell_main(int argc, char **argv)
{
rtems_printer printer;
rtems_print_printer_printf(&printer);
bsp_interrupt_report_with_plugin(&printer);
return 0;
}
struct rtems_shell_cmd_tt bsp_interrupt_shell_command = {
.name = "irq",
.usage = "Prints interrupt information",
.topic = "rtems",
.command = bsp_interrupt_shell_main,
.alias = NULL,
.next = NULL
};