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

5
Last change on this file since ac84d42e was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

  • 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.org/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.