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

4.11
Last change on this file since a9e5e74 was a9e5e74, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 6, 2012 at 9:15:17 AM

bsps: Use allocator mutex for generic IRQ support

Since the allocator mutex allows nesting now we can use it for the
generic IRQ support (recursive obtain in a potential malloc()). This
simplifies the code and reduces memory usage.

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