source: rtems/bsps/shared/irq/irq-generic.c @ 70357f1

Last change on this file since 70357f1 was 70357f1, checked in by Sebastian Huber <sebastian.huber@…>, on 06/25/21 at 13:26:45

bsps/irq: Move bsp_interrupt_handler_is_empty()

This function is only used by one BSP.

Update #3269.

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/**
4 * @file
5 *
6 * @ingroup bsp_interrupt
7 *
8 * @brief This source file contains the generic interrupt controller support
9 *   implementation.
10 */
11
12/*
13 * Copyright (C) 2008, 2018 embedded brains GmbH (http://www.embedded-brains.de)
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 *    notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 *    notice, this list of conditions and the following disclaimer in the
22 *    documentation and/or other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <bsp/irq-generic.h>
38#include <bsp/fatal.h>
39
40#include <stdlib.h>
41
42#include <rtems/malloc.h>
43
44#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
45  bsp_interrupt_handler_index_type bsp_interrupt_handler_index_table
46    [BSP_INTERRUPT_VECTOR_COUNT];
47#endif
48
49rtems_interrupt_entry bsp_interrupt_handler_table
50  [BSP_INTERRUPT_HANDLER_TABLE_SIZE];
51
52/* The last entry indicates if everything is initialized */
53uint8_t bsp_interrupt_handler_unique_table
54  [ ( BSP_INTERRUPT_HANDLER_TABLE_SIZE + 7 + 1 ) / 8 ];
55
56void bsp_interrupt_handler_empty( void *arg )
57{
58  rtems_vector_number vector = (rtems_vector_number) (uintptr_t) arg;
59
60  bsp_interrupt_handler_default( vector );
61}
62
63#ifdef RTEMS_SMP
64  static void bsp_interrupt_handler_do_nothing(void *arg)
65  {
66    (void) arg;
67  }
68#endif
69
70static inline void bsp_interrupt_set_handler_unique(
71  rtems_vector_number index,
72  bool unique
73)
74{
75  rtems_vector_number i = index / 8;
76  rtems_vector_number s = index % 8;
77  if (unique) {
78    bsp_interrupt_handler_unique_table [i] |= (uint8_t) (0x1U << s);
79  } else {
80    bsp_interrupt_handler_unique_table [i] &= (uint8_t) ~(0x1U << s);
81  }
82}
83
84static inline void bsp_interrupt_set_initialized(void)
85{
86  bsp_interrupt_set_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE, true);
87}
88
89static inline void bsp_interrupt_clear_handler_entry(
90  rtems_interrupt_entry *e,
91  rtems_vector_number vector
92)
93{
94  e->handler = bsp_interrupt_handler_empty;
95  bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
96  e->arg = (void *) (uintptr_t) 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 rtems_interrupt_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 = bsp_interrupt_handler_index(vector);
121    return true;
122  #endif
123}
124
125rtems_status_code bsp_interrupt_check_and_lock(
126  rtems_vector_number     vector,
127  rtems_interrupt_handler handler
128)
129{
130  if ( !bsp_interrupt_is_initialized() ) {
131    return RTEMS_INCORRECT_STATE;
132  }
133
134  if ( handler == NULL ) {
135    return RTEMS_INVALID_ADDRESS;
136  }
137
138  if ( !bsp_interrupt_is_valid_vector( vector ) ) {
139    return RTEMS_INVALID_ID;
140  }
141
142  if ( rtems_interrupt_is_in_progress() ) {
143    return RTEMS_CALLED_FROM_ISR;
144  }
145
146  bsp_interrupt_lock();
147
148  return RTEMS_SUCCESSFUL;
149}
150
151void bsp_interrupt_initialize(void)
152{
153  rtems_status_code sc = RTEMS_SUCCESSFUL;
154  size_t i = 0;
155
156  /* Initialize handler table */
157  for (i = 0; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
158    bsp_interrupt_handler_table [i].handler = bsp_interrupt_handler_empty;
159    bsp_interrupt_handler_table [i].arg = (void *) i;
160  }
161
162  sc = bsp_interrupt_facility_initialize();
163  if (sc != RTEMS_SUCCESSFUL) {
164    bsp_fatal(BSP_FATAL_INTERRUPT_INITIALIZATION);
165  }
166
167  bsp_interrupt_set_initialized();
168}
169
170/**
171 * @brief Installs an interrupt handler.
172 *
173 * @ingroup bsp_interrupt
174 *
175 * @return In addition to the standard status codes this function returns:
176 * - If the BSP interrupt support is not initialized RTEMS_INTERNAL_ERROR will
177 * be returned.
178 * - If not enough memory for a new handler is available RTEMS_NO_MEMORY will
179 * be returned
180 *
181 * @see rtems_interrupt_handler_install()
182 */
183static rtems_status_code bsp_interrupt_handler_install(
184  rtems_vector_number vector,
185  const char *info,
186  rtems_option options,
187  rtems_interrupt_handler handler,
188  void *arg
189)
190{
191  rtems_status_code sc;
192  rtems_interrupt_level level;
193  rtems_vector_number index = 0;
194  rtems_interrupt_entry *head = NULL;
195  bool enable_vector = false;
196  bool replace = RTEMS_INTERRUPT_IS_REPLACE(options);
197
198  sc = bsp_interrupt_check_and_lock( vector, handler );
199
200  if ( sc != RTEMS_SUCCESSFUL ) {
201    return sc;
202  }
203
204  /* Get handler table index */
205  index = bsp_interrupt_handler_index(vector);
206
207  /* Get head entry of the handler list for current vector */
208  head = &bsp_interrupt_handler_table [index];
209
210  if (bsp_interrupt_is_empty_handler_entry(head)) {
211    if (replace) {
212      /* No handler to replace exists */
213      bsp_interrupt_unlock();
214      return RTEMS_UNSATISFIED;
215    }
216
217    /*
218     * No real handler installed yet.  So allocate a new index in
219     * the handler table and fill the entry with life.
220     */
221    if (bsp_interrupt_allocate_handler_index(vector, &index)) {
222      bsp_interrupt_disable(level);
223      bsp_interrupt_handler_table [index].arg = arg;
224      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
225      bsp_interrupt_handler_table [index].handler = handler;
226      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
227        bsp_interrupt_handler_index_table [vector] = index;
228      #endif
229      bsp_interrupt_enable(level);
230      bsp_interrupt_handler_table [index].info = info;
231    } else {
232      /* Handler table is full */
233      bsp_interrupt_unlock();
234      return RTEMS_NO_MEMORY;
235    }
236
237    /* This is the first handler so enable the vector later */
238    enable_vector = true;
239  } else {
240    rtems_interrupt_entry *current = head;
241    rtems_interrupt_entry *tail = NULL;
242    rtems_interrupt_entry *match = NULL;
243
244    /* Ensure that a unique handler remains unique */
245    if (
246      !replace
247        && (RTEMS_INTERRUPT_IS_UNIQUE(options)
248          || bsp_interrupt_is_handler_unique(index))
249    ) {
250      /*
251       * Tried to install a unique handler on a not empty
252       * list or there is already a unique handler installed.
253       */
254      bsp_interrupt_unlock();
255      return RTEMS_RESOURCE_IN_USE;
256    }
257
258    /*
259     * Search for the list tail and check if the handler is already
260     * installed.
261     */
262    do {
263      if (
264        match == NULL
265          && (current->handler == handler || replace)
266          && current->arg == arg
267      ) {
268        match = current;
269      }
270      tail = current;
271      current = current->next;
272    } while (current != NULL);
273
274    if (replace) {
275      /* Ensure that a handler to replace exists */
276      if (match == NULL) {
277        bsp_interrupt_unlock();
278        return RTEMS_UNSATISFIED;
279      }
280
281      /* Use existing entry */
282      current = match;
283    } else {
284      /* Ensure the handler is not already installed */
285      if (match != NULL) {
286        /* The handler is already installed */
287        bsp_interrupt_unlock();
288        return RTEMS_TOO_MANY;
289      }
290
291      /* Allocate a new entry */
292      current = rtems_malloc(sizeof(*current));
293      if (current == NULL) {
294        /* Not enough memory */
295        bsp_interrupt_unlock();
296        return RTEMS_NO_MEMORY;
297      }
298    }
299
300    /* Update existing entry or set new entry */
301    current->handler = handler;
302    current->info = info;
303
304    if (!replace) {
305      /* Set new entry */
306      current->arg = arg;
307      current->next = NULL;
308
309      /* Link to list tail */
310      bsp_interrupt_disable(level);
311      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
312      tail->next = current;
313      bsp_interrupt_enable(level);
314    }
315  }
316
317  /* Make the handler unique if necessary */
318  bsp_interrupt_set_handler_unique(index, RTEMS_INTERRUPT_IS_UNIQUE(options));
319
320  /* Enable the vector if necessary */
321  if (enable_vector) {
322    bsp_interrupt_vector_enable(vector);
323  }
324
325  /* Unlock */
326  bsp_interrupt_unlock();
327
328  return RTEMS_SUCCESSFUL;
329}
330
331/**
332 * @brief Removes an interrupt handler.
333 *
334 * @ingroup bsp_interrupt
335 *
336 * @return In addition to the standard status codes this function returns
337 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
338 *
339 * @see rtems_interrupt_handler_remove().
340 */
341static rtems_status_code bsp_interrupt_handler_remove(
342  rtems_vector_number vector,
343  rtems_interrupt_handler handler,
344  void *arg
345)
346{
347  rtems_status_code sc;
348  rtems_interrupt_level level;
349  rtems_vector_number index = 0;
350  rtems_interrupt_entry *head = NULL;
351  rtems_interrupt_entry *current = NULL;
352  rtems_interrupt_entry *previous = NULL;
353  rtems_interrupt_entry *match = NULL;
354
355  sc = bsp_interrupt_check_and_lock( vector, handler );
356
357  if ( sc != RTEMS_SUCCESSFUL ) {
358    return sc;
359  }
360
361  /* Get handler table index */
362  index = bsp_interrupt_handler_index(vector);
363
364  /* Get head entry of the handler list for current vector */
365  head = &bsp_interrupt_handler_table [index];
366
367  /* Search for a matching entry */
368  current = head;
369  do {
370    if (current->handler == handler && current->arg == arg) {
371      match = current;
372      break;
373    }
374    previous = current;
375    current = current->next;
376  } while (current != NULL);
377
378  /* Remove the matching entry */
379  if (match != NULL) {
380    if (match->next != NULL) {
381      /*
382       * The match has a successor.  A successor is always
383       * allocated.  So replace the match with its successor
384       * and free the successor entry.
385       */
386      current = match->next;
387
388      bsp_interrupt_disable(level);
389      #ifdef RTEMS_SMP
390        match->handler = bsp_interrupt_handler_do_nothing;
391        bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
392      #endif
393      match->arg = current->arg;
394      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
395      match->handler = current->handler;
396      match->info = current->info;
397      match->next = current->next;
398      bsp_interrupt_enable(level);
399
400      free(current);
401    } else if (match == head) {
402      /*
403       * The match is the list head and has no successor.
404       * The list head is stored in a static table so clear
405       * this entry.  Since now the list is empty disable the
406       * vector.
407       */
408
409      /* Disable the vector */
410      bsp_interrupt_vector_disable(vector);
411
412      /* Clear entry */
413      bsp_interrupt_disable(level);
414      bsp_interrupt_clear_handler_entry(head, vector);
415      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
416        bsp_interrupt_handler_index_table [vector] = 0;
417      #endif
418      bsp_interrupt_enable(level);
419
420      /* Allow shared handlers */
421      bsp_interrupt_set_handler_unique(index, false);
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      bsp_interrupt_disable(level);
428      previous->next = NULL;
429      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
430      bsp_interrupt_enable(level);
431
432      free(match);
433    }
434  } else {
435    /* No matching entry found */
436    bsp_interrupt_unlock();
437    return RTEMS_UNSATISFIED;
438  }
439
440  /* Unlock */
441  bsp_interrupt_unlock();
442
443  return RTEMS_SUCCESSFUL;
444}
445
446rtems_status_code rtems_interrupt_handler_install(
447  rtems_vector_number vector,
448  const char *info,
449  rtems_option options,
450  rtems_interrupt_handler handler,
451  void *arg
452)
453{
454  return bsp_interrupt_handler_install(vector, info, options, handler, arg);
455}
456
457rtems_status_code rtems_interrupt_handler_remove(
458  rtems_vector_number vector,
459  rtems_interrupt_handler handler,
460  void *arg
461)
462{
463  return bsp_interrupt_handler_remove(vector, handler, arg);
464}
Note: See TracBrowser for help on using the repository browser.