source: rtems/c/src/lib/libbsp/shared/src/irq-generic.c @ 718124e

4.115
Last change on this file since 718124e was 718124e, checked in by Sebastian Huber <sebastian.huber@…>, on 03/03/14 at 09:18:01

rtems: Add RTEMS_INTERRUPT_REPLACE

A new option RTEMS_INTERRUPT_REPLACE is introduced that permits updating
the first interrupt handler for the registered interrupt vector and
matching argument. If no match is found, the install function fails
with RTEMS_UNSATISFIED.

The Interrupt Manager Extension offers interrupt handlers with an
argument pointer. It is impossible to update two words (handler and
argument) atomically on most architectures. In order to avoid an SMP
lock in bsp_interrupt_handler_dispatch() which would degrade the
interrupt response time an alternative must be provided that makes it
possible to tear-down interrupt sources without an SMP lock.

Add RTEMS_INTERRUPT_REPLACE option to Interrupt Manager Extension. This
enables a clean tear-down of interrupt sources on SMP configurations.
Instead of an interrupt handler removal a replacement handler can be
installed to silence an interrupt source. This can be used in contexts
that allow no sophisticated synchronization (e.g. in atexit() or fatal
handlers).

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