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

4.104.115
Last change on this file since 4a6d160 was 4a6d160, checked in by Joel Sherrill <joel.sherrill@…>, on 05/15/09 at 12:53:44

2009-05-15 Sebastian Huber <sebastian.huber@…>

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