/** * @file * * @ingroup bsp_interrupt * * @brief Source file for generic BSP interrupt support. */ /* * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette. * * Copyright (c) 2008 * 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.com/license/LICENSE. */ #include #ifdef BSP_INTERRUPT_USE_INDEX_TABLE bsp_interrupt_handler_index_type bsp_interrupt_handler_index_table [BSP_INTERRUPT_VECTOR_NUMBER]; #endif /* BSP_INTERRUPT_USE_INDEX_TABLE */ bsp_interrupt_handler_entry bsp_interrupt_handler_table [BSP_INTERRUPT_HANDLER_TABLE_SIZE] = { [0 ... BSP_INTERRUPT_HANDLER_TABLE_SIZE - 1] = { .handler = bsp_interrupt_handler_empty, .arg = NULL, .info = NULL, .next = NULL } }; /* 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 rtems_id bsp_interrupt_mutex = RTEMS_ID_NONE; 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); } void bsp_interrupt_handler_empty( rtems_vector_number vector, void *arg) { /* Do nothing */ } static inline void bsp_interrupt_clear_handler_entry( bsp_interrupt_handler_entry *e) { e->handler = bsp_interrupt_handler_empty; e->arg = NULL; 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) { if (bsp_interrupt_is_empty_handler_entry( &bsp_interrupt_handler_table [i])) { *index = i; return 1; } } return 0; #else /* BSP_INTERRUPT_USE_INDEX_TABLE */ *index = vector; return 1; #endif /* BSP_INTERRUPT_USE_INDEX_TABLE */ } static bsp_interrupt_handler_entry *bsp_interrupt_allocate_handler_entry() { #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 /* BSP_INTERRUPT_NO_HEAP_USAGE */ return malloc( sizeof( bsp_interrupt_handler_entry)); #endif /* BSP_INTERRUPT_NO_HEAP_USAGE */ } static void bsp_interrupt_free_handler_entry( bsp_interrupt_handler_entry *e) { #ifdef BSP_INTERRUPT_NO_HEAP_USAGE bsp_interrupt_clear_handler_entry( e); #else /* BSP_INTERRUPT_NO_HEAP_USAGE */ free( e); #endif /* BSP_INTERRUPT_NO_HEAP_USAGE */ } static rtems_status_code bsp_interrupt_lock() { rtems_status_code sc = RTEMS_SUCCESSFUL; if (_System_state_Is_up( _System_state_Get())) { if (bsp_interrupt_mutex == RTEMS_ID_NONE) { rtems_id mutex = RTEMS_ID_NONE; rtems_interrupt_level level; /* Create a mutex */ sc = rtems_semaphore_create ( rtems_build_name( 'I', 'N', 'T', 'R'), 1, RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY, RTEMS_NO_PRIORITY, &mutex ); if (sc != RTEMS_SUCCESSFUL) { return sc; } /* Assign the mutex */ rtems_interrupt_disable( level); if (bsp_interrupt_mutex == RTEMS_ID_NONE) { /* Nobody else assigned the mutex in the meantime */ bsp_interrupt_mutex = mutex; rtems_interrupt_enable( level); } else { /* Somebody else won */ rtems_interrupt_enable( level); sc = rtems_semaphore_delete( mutex); if (sc != RTEMS_SUCCESSFUL) { return sc; } } } return rtems_semaphore_obtain( bsp_interrupt_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); } else { return RTEMS_SUCCESSFUL; } } static rtems_status_code bsp_interrupt_unlock() { if (bsp_interrupt_mutex != RTEMS_ID_NONE) { return rtems_semaphore_release( bsp_interrupt_mutex); } else { return RTEMS_SUCCESSFUL; } } /** * @brief Initialize BSP interrupt support. * * You must call this function before you can install, remove and dispatch * interrupt handlers. The BSP specific bsp_interrupt_facility_initialize() * function will be called after all internals are initialized. Initialization * is complete if everything was successful. */ rtems_status_code bsp_interrupt_initialize() { rtems_status_code sc = RTEMS_SUCCESSFUL; rtems_vector_number index = 0; /* Lock */ sc = bsp_interrupt_lock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } /* Check if already initialized */ if (bsp_interrupt_is_initialized()) { bsp_interrupt_unlock(); return RTEMS_INTERNAL_ERROR; } /* BSP specific initialization */ sc = bsp_interrupt_facility_initialize(); if (sc != RTEMS_SUCCESSFUL) { bsp_interrupt_unlock(); return sc; } /* Now we are initialized */ bsp_interrupt_set_initialized(); /* Unlock */ sc = bsp_interrupt_unlock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } return RTEMS_SUCCESSFUL; } /** * @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_status_code sc = RTEMS_SUCCESSFUL; rtems_interrupt_level level; rtems_vector_number index = 0; bsp_interrupt_handler_entry *head = NULL; bsp_interrupt_handler_entry *tail = NULL; bsp_interrupt_handler_entry *current = NULL; bsp_interrupt_handler_entry *match = NULL; bool enable_vector = false; /* 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_NUMBER; } else if (handler == NULL) { return RTEMS_INVALID_ADDRESS; } else if (rtems_interrupt_is_in_progress()) { return RTEMS_CALLED_FROM_ISR; } /* Lock */ sc = bsp_interrupt_lock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } /* 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)) { /* * 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)) { rtems_interrupt_disable( level); bsp_interrupt_handler_table [index].handler = handler; bsp_interrupt_handler_table [index].arg = arg; #ifdef BSP_INTERRUPT_USE_INDEX_TABLE bsp_interrupt_handler_index_table [vector] = index; #endif /* BSP_INTERRUPT_USE_INDEX_TABLE */ rtems_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 { /* Ensure that a unique handler remains unique */ if (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. */ current = head; do { if (current->handler == handler && current->arg == arg) { match = current; } tail = current; current = current->next; } while (current != NULL); /* 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; } /* Set entry */ current->handler = handler; current->arg = arg; current->info = info; current->next = NULL; /* Link to list tail */ rtems_interrupt_disable( level); tail->next = current; rtems_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) { sc = bsp_interrupt_vector_enable( vector); if (sc != RTEMS_SUCCESSFUL) { bsp_interrupt_unlock(); return sc; } } /* Unlock */ sc = bsp_interrupt_unlock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } 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_status_code sc = RTEMS_SUCCESSFUL; 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_NUMBER; } else if (handler == NULL) { return RTEMS_INVALID_ADDRESS; } else if (rtems_interrupt_is_in_progress()) { return RTEMS_CALLED_FROM_ISR; } /* Lock */ sc = bsp_interrupt_lock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } /* 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; rtems_interrupt_disable( level); *match = *current; rtems_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 */ sc = bsp_interrupt_vector_disable( vector); /* Clear entry */ rtems_interrupt_disable( level); bsp_interrupt_clear_handler_entry( head); #ifdef BSP_INTERRUPT_USE_INDEX_TABLE bsp_interrupt_handler_index_table [vector] = 0; #endif /* BSP_INTERRUPT_USE_INDEX_TABLE */ rtems_interrupt_enable( level); /* Allow shared handlers */ bsp_interrupt_set_handler_unique( index, false); /* Check status code */ if (sc != RTEMS_SUCCESSFUL) { bsp_interrupt_unlock(); return sc; } } else { /* * The match is the list tail and has a predecessor. * So terminate the predecessor and free the match. */ rtems_interrupt_disable( level); previous->next = NULL; rtems_interrupt_enable( level); bsp_interrupt_free_handler_entry( match); } } else { /* No matching entry found */ bsp_interrupt_unlock(); return RTEMS_UNSATISFIED; } /* Unlock */ sc = bsp_interrupt_unlock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } 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) { rtems_status_code sc = RTEMS_SUCCESSFUL; 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_NUMBER; } else if (rtems_interrupt_is_in_progress()) { return RTEMS_CALLED_FROM_ISR; } /* Lock */ sc = bsp_interrupt_lock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } /* 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 */ sc = bsp_interrupt_unlock(); if (sc != RTEMS_SUCCESSFUL) { return sc; } 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); }