source: rtems/c/src/lib/libbsp/shared/src/irq-generic.c @ 6faf789

4.115
Last change on this file since 6faf789 was 6faf789, checked in by Sebastian Huber <sebastian.huber@…>, on 02/26/14 at 14:45:02

bsps: Fix empty interrupt handler entry

The vector number of spurious interrupts was wrong after the interrupt
handler removal on SMP configurations.

  • Property mode set to 100644
File size: 13.4 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup bsp_interrupt
5 *
6 * @brief Generic BSP interrupt support implementation.
7 */
8
9/*
10 * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette.
11 *
12 * Copyright (c) 2008-2012 embedded brains GmbH.
13 *
14 *  embedded brains GmbH
15 *  Obere Lagerstr. 30
16 *  82178 Puchheim
17 *  Germany
18 *  <rtems@embedded-brains.de>
19 *
20 * The license and distribution terms for this file may be
21 * found in the file LICENSE in this distribution or at
22 * http://www.rtems.com/license/LICENSE.
23 */
24
25#include <bsp/irq-generic.h>
26#include <bsp/fatal.h>
27
28#include <stdlib.h>
29
30#include <rtems/score/apimutex.h>
31#include <rtems/score/sysstate.h>
32
33#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
34  bsp_interrupt_handler_index_type bsp_interrupt_handler_index_table
35    [BSP_INTERRUPT_VECTOR_NUMBER];
36#endif
37
38bsp_interrupt_handler_entry bsp_interrupt_handler_table
39  [BSP_INTERRUPT_HANDLER_TABLE_SIZE];
40
41/* The last entry indicates if everything is initialized */
42static uint8_t bsp_interrupt_handler_unique_table
43  [(BSP_INTERRUPT_HANDLER_TABLE_SIZE + 7 + 1) / 8];
44
45static void bsp_interrupt_handler_empty(void *arg)
46{
47  rtems_vector_number vector = (rtems_vector_number) arg;
48
49  bsp_interrupt_handler_default(vector);
50}
51
52static inline bool bsp_interrupt_is_handler_unique(rtems_vector_number index)
53{
54  rtems_vector_number i = index / 8;
55  rtems_vector_number s = index % 8;
56  return (bsp_interrupt_handler_unique_table [i] >> s) & 0x1;
57}
58
59static inline void bsp_interrupt_set_handler_unique(
60  rtems_vector_number index,
61  bool unique
62)
63{
64  rtems_vector_number i = index / 8;
65  rtems_vector_number s = index % 8;
66  if (unique) {
67    bsp_interrupt_handler_unique_table [i] |= (uint8_t) (0x1U << s);
68  } else {
69    bsp_interrupt_handler_unique_table [i] &= (uint8_t) ~(0x1U << s);
70  }
71}
72
73static inline bool bsp_interrupt_is_initialized(void)
74{
75  return bsp_interrupt_is_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE);
76}
77
78static inline void bsp_interrupt_set_initialized(void)
79{
80  bsp_interrupt_set_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE, true);
81}
82
83static inline bool bsp_interrupt_is_empty_handler_entry(
84  const bsp_interrupt_handler_entry *e
85)
86{
87  return e->handler == bsp_interrupt_handler_empty;
88}
89
90static inline void bsp_interrupt_clear_handler_entry(
91  bsp_interrupt_handler_entry *e,
92  rtems_vector_number vector
93)
94{
95  e->handler = bsp_interrupt_handler_empty;
96  e->arg = (void *) vector;
97  e->info = NULL;
98  e->next = NULL;
99}
100
101static inline bool bsp_interrupt_allocate_handler_index(
102  rtems_vector_number vector,
103  rtems_vector_number *index
104)
105{
106  #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
107    rtems_vector_number i = 0;
108
109    /* The first entry will remain empty */
110    for (i = 1; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
111      const bsp_interrupt_handler_entry *e = &bsp_interrupt_handler_table [i];
112      if (bsp_interrupt_is_empty_handler_entry(e)) {
113        *index = i;
114        return true;
115      }
116    }
117
118    return false;
119  #else
120    *index = vector;
121    return true;
122  #endif
123}
124
125static bsp_interrupt_handler_entry *bsp_interrupt_allocate_handler_entry(void)
126{
127  #ifdef BSP_INTERRUPT_NO_HEAP_USAGE
128    rtems_vector_number index = 0;
129    if (bsp_interrupt_allocate_handler_index(0, &index)) {
130      return &bsp_interrupt_handler_table [index];
131    } else {
132      return NULL;
133    }
134  #else
135    return malloc(sizeof(bsp_interrupt_handler_entry));
136  #endif
137}
138
139static void bsp_interrupt_free_handler_entry(bsp_interrupt_handler_entry *e)
140{
141  #ifdef BSP_INTERRUPT_NO_HEAP_USAGE
142    bsp_interrupt_clear_handler_entry(e, 0);
143  #else
144    free(e);
145  #endif
146}
147
148static void bsp_interrupt_lock(void)
149{
150  if (_System_state_Is_up(_System_state_Get())) {
151    _RTEMS_Lock_allocator();
152  }
153}
154
155static void bsp_interrupt_unlock(void)
156{
157  if (_System_state_Is_up(_System_state_Get())) {
158    _RTEMS_Unlock_allocator();
159  }
160}
161
162void bsp_interrupt_initialize(void)
163{
164  rtems_status_code sc = RTEMS_SUCCESSFUL;
165  size_t i = 0;
166
167  /* Initialize handler table */
168  for (i = 0; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
169    bsp_interrupt_handler_table [i].handler = bsp_interrupt_handler_empty;
170    bsp_interrupt_handler_table [i].arg = (void *) i;
171  }
172
173  sc = bsp_interrupt_facility_initialize();
174  if (sc != RTEMS_SUCCESSFUL) {
175    bsp_fatal(BSP_FATAL_INTERRUPT_INITIALIZATION);
176  }
177
178  bsp_interrupt_set_initialized();
179}
180
181/**
182 * @brief Installs an interrupt handler.
183 *
184 * @ingroup bsp_interrupt
185 *
186 * @return In addition to the standard status codes this function returns:
187 * - If the BSP interrupt support is not initialized RTEMS_INTERNAL_ERROR will
188 * be returned.
189 * - If not enough memory for a new handler is available RTEMS_NO_MEMORY will
190 * be returned
191 *
192 * @see rtems_interrupt_handler_install()
193 */
194static rtems_status_code bsp_interrupt_handler_install(
195  rtems_vector_number vector,
196  const char *info,
197  rtems_option options,
198  rtems_interrupt_handler handler,
199  void *arg
200)
201{
202  rtems_status_code sc = RTEMS_SUCCESSFUL;
203  rtems_interrupt_level level;
204  rtems_vector_number index = 0;
205  bsp_interrupt_handler_entry *head = NULL;
206  bsp_interrupt_handler_entry *tail = NULL;
207  bsp_interrupt_handler_entry *current = NULL;
208  bsp_interrupt_handler_entry *match = NULL;
209  bool enable_vector = false;
210
211  /* Check parameters and system state */
212  if (!bsp_interrupt_is_initialized()) {
213    return RTEMS_INTERNAL_ERROR;
214  } else if (!bsp_interrupt_is_valid_vector(vector)) {
215    return RTEMS_INVALID_ID;
216  } else if (handler == NULL) {
217    return RTEMS_INVALID_ADDRESS;
218  } else if (rtems_interrupt_is_in_progress()) {
219    return RTEMS_CALLED_FROM_ISR;
220  }
221
222  /* Lock */
223  bsp_interrupt_lock();
224
225  /* Get handler table index */
226  index = bsp_interrupt_handler_index(vector);
227
228  /* Get head entry of the handler list for current vector */
229  head = &bsp_interrupt_handler_table [index];
230
231  if (bsp_interrupt_is_empty_handler_entry(head)) {
232    /*
233     * No real handler installed yet.  So allocate a new index in
234     * the handler table and fill the entry with life.
235     */
236    if (bsp_interrupt_allocate_handler_index(vector, &index)) {
237      rtems_interrupt_disable(level);
238      bsp_interrupt_handler_table [index].handler = handler;
239      bsp_interrupt_handler_table [index].arg = arg;
240      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
241        bsp_interrupt_handler_index_table [vector] = index;
242      #endif
243      rtems_interrupt_enable(level);
244      bsp_interrupt_handler_table [index].info = info;
245    } else {
246      /* Handler table is full */
247      bsp_interrupt_unlock();
248      return RTEMS_NO_MEMORY;
249    }
250
251    /* This is the first handler so enable the vector later */
252    enable_vector = true;
253  } else {
254    /* Ensure that a unique handler remains unique */
255    if (
256      RTEMS_INTERRUPT_IS_UNIQUE(options)
257        || bsp_interrupt_is_handler_unique(index)
258    ) {
259      /*
260       * Tried to install a unique handler on a not empty
261       * list or there is already a unique handler installed.
262       */
263      bsp_interrupt_unlock();
264      return RTEMS_RESOURCE_IN_USE;
265    }
266
267    /*
268     * Search for the list tail and check if the handler is already
269     * installed.
270     */
271    current = head;
272    do {
273      if (current->handler == handler && current->arg == arg) {
274        match = current;
275      }
276      tail = current;
277      current = current->next;
278    } while (current != NULL);
279
280    /* Ensure the handler is not already installed */
281    if (match != NULL) {
282      /* The handler is already installed */
283      bsp_interrupt_unlock();
284      return RTEMS_TOO_MANY;
285    }
286
287    /* Allocate a new entry */
288    current = bsp_interrupt_allocate_handler_entry();
289    if (current == NULL) {
290      /* Not enough memory */
291      bsp_interrupt_unlock();
292      return RTEMS_NO_MEMORY;
293    }
294
295    /* Set entry */
296    current->handler = handler;
297    current->arg = arg;
298    current->info = info;
299    current->next = NULL;
300
301    /* Link to list tail */
302    rtems_interrupt_disable(level);
303    tail->next = current;
304    rtems_interrupt_enable(level);
305  }
306
307  /* Make the handler unique if necessary */
308  bsp_interrupt_set_handler_unique(index, RTEMS_INTERRUPT_IS_UNIQUE(options));
309
310  /* Enable the vector if necessary */
311  if (enable_vector) {
312    sc = bsp_interrupt_vector_enable(vector);
313    if (sc != RTEMS_SUCCESSFUL) {
314      bsp_interrupt_unlock();
315      return sc;
316    }
317  }
318
319  /* Unlock */
320  bsp_interrupt_unlock();
321
322  return RTEMS_SUCCESSFUL;
323}
324
325/**
326 * @brief Removes an interrupt handler.
327 *
328 * @ingroup bsp_interrupt
329 *
330 * @return In addition to the standard status codes this function returns
331 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
332 *
333 * @see rtems_interrupt_handler_remove().
334 */
335static rtems_status_code bsp_interrupt_handler_remove(
336  rtems_vector_number vector,
337  rtems_interrupt_handler handler,
338  void *arg
339)
340{
341  rtems_status_code sc = RTEMS_SUCCESSFUL;
342  rtems_interrupt_level level;
343  rtems_vector_number index = 0;
344  bsp_interrupt_handler_entry *head = NULL;
345  bsp_interrupt_handler_entry *current = NULL;
346  bsp_interrupt_handler_entry *previous = NULL;
347  bsp_interrupt_handler_entry *match = NULL;
348
349  /* Check parameters and system state */
350  if (!bsp_interrupt_is_initialized()) {
351    return RTEMS_INTERNAL_ERROR;
352  } else if (!bsp_interrupt_is_valid_vector(vector)) {
353    return RTEMS_INVALID_ID;
354  } else if (handler == NULL) {
355    return RTEMS_INVALID_ADDRESS;
356  } else if (rtems_interrupt_is_in_progress()) {
357    return RTEMS_CALLED_FROM_ISR;
358  }
359
360  /* Lock */
361  bsp_interrupt_lock();
362
363  /* Get handler table index */
364  index = bsp_interrupt_handler_index(vector);
365
366  /* Get head entry of the handler list for current vector */
367  head = &bsp_interrupt_handler_table [index];
368
369  /* Search for a matching entry */
370  current = head;
371  do {
372    if (current->handler == handler && current->arg == arg) {
373      match = current;
374      break;
375    }
376    previous = current;
377    current = current->next;
378  } while (current != NULL);
379
380  /* Remove the matching entry */
381  if (match != NULL) {
382    if (match->next != NULL) {
383      /*
384       * The match has a successor.  A successor is always
385       * allocated.  So replace the match with its successor
386       * and free the successor entry.
387       */
388      current = match->next;
389
390      rtems_interrupt_disable(level);
391      *match = *current;
392      rtems_interrupt_enable(level);
393
394      bsp_interrupt_free_handler_entry(current);
395    } else if (match == head) {
396      /*
397       * The match is the list head and has no successor.
398       * The list head is stored in a static table so clear
399       * this entry.  Since now the list is empty disable the
400       * vector.
401       */
402
403      /* Disable the vector */
404      sc = bsp_interrupt_vector_disable(vector);
405
406      /* Clear entry */
407      rtems_interrupt_disable(level);
408      bsp_interrupt_clear_handler_entry(head, vector);
409      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
410        bsp_interrupt_handler_index_table [vector] = 0;
411      #endif
412      rtems_interrupt_enable(level);
413
414      /* Allow shared handlers */
415      bsp_interrupt_set_handler_unique(index, false);
416
417      /* Check status code */
418      if (sc != RTEMS_SUCCESSFUL) {
419        bsp_interrupt_unlock();
420        return sc;
421      }
422    } else {
423      /*
424       * The match is the list tail and has a predecessor.
425       * So terminate the predecessor and free the match.
426       */
427      rtems_interrupt_disable(level);
428      previous->next = NULL;
429      rtems_interrupt_enable(level);
430
431      bsp_interrupt_free_handler_entry(match);
432    }
433  } else {
434    /* No matching entry found */
435    bsp_interrupt_unlock();
436    return RTEMS_UNSATISFIED;
437  }
438
439  /* Unlock */
440  bsp_interrupt_unlock();
441
442  return RTEMS_SUCCESSFUL;
443}
444
445/**
446 * @brief Iterates over all installed interrupt handler of a vector.
447 *
448 * @ingroup bsp_interrupt
449 *
450 * @return In addition to the standard status codes this function returns
451 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
452 *
453 * @see rtems_interrupt_handler_iterate().
454 */
455static rtems_status_code bsp_interrupt_handler_iterate(
456  rtems_vector_number vector,
457  rtems_interrupt_per_handler_routine routine,
458  void *arg
459)
460{
461  bsp_interrupt_handler_entry *current = NULL;
462  rtems_option options = 0;
463  rtems_vector_number index = 0;
464
465  /* Check parameters and system state */
466  if (!bsp_interrupt_is_initialized()) {
467    return RTEMS_INTERNAL_ERROR;
468  } else if (!bsp_interrupt_is_valid_vector(vector)) {
469    return RTEMS_INVALID_ID;
470  } else if (rtems_interrupt_is_in_progress()) {
471    return RTEMS_CALLED_FROM_ISR;
472  }
473
474  /* Lock */
475  bsp_interrupt_lock();
476
477  /* Interate */
478  index = bsp_interrupt_handler_index(vector);
479  current = &bsp_interrupt_handler_table [index];
480  if (!bsp_interrupt_is_empty_handler_entry(current)) {
481    do {
482      options = bsp_interrupt_is_handler_unique(index) ?
483        RTEMS_INTERRUPT_UNIQUE : RTEMS_INTERRUPT_SHARED;
484      routine(arg, current->info, options, current->handler, current->arg);
485      current = current->next;
486    } while (current != NULL);
487  }
488
489  /* Unlock */
490  bsp_interrupt_unlock();
491
492  return RTEMS_SUCCESSFUL;
493}
494
495rtems_status_code rtems_interrupt_handler_install(
496  rtems_vector_number vector,
497  const char *info,
498  rtems_option options,
499  rtems_interrupt_handler handler,
500  void *arg
501)
502{
503  return bsp_interrupt_handler_install(vector, info, options, handler, arg);
504}
505
506rtems_status_code rtems_interrupt_handler_remove(
507  rtems_vector_number vector,
508  rtems_interrupt_handler handler,
509  void *arg
510)
511{
512  return bsp_interrupt_handler_remove(vector, handler, arg);
513}
514
515rtems_status_code rtems_interrupt_handler_iterate(
516  rtems_vector_number vector,
517  rtems_interrupt_per_handler_routine routine,
518  void *arg
519)
520{
521  return bsp_interrupt_handler_iterate(vector, routine, arg);
522}
Note: See TracBrowser for help on using the repository browser.