nuttx/sched/irq/irq_chain.c
hujun5 572daf46c2 irq: add isr thread
purpose:
To improve the real-time performance of the system, we prefer to perform
as few operations as possible within the interrupt function.
We have designed an interrupt thread for each interrupt,
where all the operations that are not necessary to be handled
in the interrupt function are delegated to be processed by the interrupt thread.
Up_enable_irq will be invoked after isrthread started.

Configuring NuttX and compile:
$ ./tools/configure.sh -l qemu-armv8a:nsh_smp
$ make
Running with qemu
$ qemu-system-aarch64 -cpu cortex-a53 -smp 4 -nographic \
   -machine virt,virtualization=on,gic-version=3 \
   -net none -chardev stdio,id=con,mux=on -serial chardev:con \
   -mon chardev=con,mode=readline -kernel ./nuttx

Signed-off-by: hujun5 <hujun5@xiaomi.com>
2024-08-27 21:49:53 +08:00

266 lines
6.8 KiB
C

/****************************************************************************
* sched/irq/irq_chain.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include "irq/irq.h"
/****************************************************************************
* Private Types
****************************************************************************/
struct irqchain_s
{
FAR struct irqchain_s *next;
xcpt_t handler; /* Address of the interrupt handler */
FAR void *arg; /* The argument provided to the interrupt handler. */
};
/****************************************************************************
* Private Data
****************************************************************************/
/* g_irqchainpool is a list of pre-allocated irq chain. The number of irq
* chains in the pool is a configuration item.
*/
static struct irqchain_s g_irqchainpool[CONFIG_PREALLOC_IRQCHAIN];
/* The g_irqchainfreelist data structure is a single linked list of irqchains
* available to the system for delayed function use.
*/
static sq_queue_t g_irqchainfreelist;
/****************************************************************************
* Private Functions
****************************************************************************/
static void irqchain_detach_all(int ndx)
{
FAR struct irqchain_s *curr;
FAR struct irqchain_s *prev;
g_irqvector[ndx].handler = irq_unexpected_isr;
curr = g_irqvector[ndx].arg;
while (curr != NULL)
{
prev = curr;
curr = curr->next;
sq_addlast((FAR struct sq_entry_s *)prev, &g_irqchainfreelist);
}
}
static int irqchain_dispatch(int irq, FAR void *context, FAR void *arg)
{
FAR struct irqchain_s *curr;
FAR struct irqchain_s *prev;
int ndx;
int ret = 0;
#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE
ndx = g_irqmap[irq];
#else
ndx = irq;
#endif
curr = g_irqvector[ndx].arg;
while (curr != NULL)
{
prev = curr;
curr = curr->next;
ret |= prev->handler(irq, context, prev->arg);
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
void irqchain_initialize(void)
{
FAR struct irqchain_s *irqchain = g_irqchainpool;
int i;
/* Initialize irqchain free lists */
sq_init(&g_irqchainfreelist);
/* The g_irqchainfreelist must be loaded at initialization time to hold the
* configured number of irqchain.
*/
for (i = 0; i < CONFIG_PREALLOC_IRQCHAIN; i++)
{
sq_addlast((FAR struct sq_entry_s *)irqchain++, &g_irqchainfreelist);
}
}
bool is_irqchain(int ndx, xcpt_t isr)
{
if (g_irqvector[ndx].handler == irq_unexpected_isr ||
g_irqvector[ndx].handler == NULL)
{
return false;
}
else if (g_irqvector[ndx].handler == irqchain_dispatch)
{
return true;
}
else
{
return isr != irq_unexpected_isr;
}
}
int irqchain_attach(int ndx, xcpt_t isr, FAR void *arg)
{
FAR struct irqchain_s *node;
FAR struct irqchain_s *curr;
if (isr != irq_unexpected_isr)
{
if (g_irqvector[ndx].handler != irqchain_dispatch)
{
if (sq_count(&g_irqchainfreelist) < 2)
{
return -ENOMEM;
}
node = (FAR struct irqchain_s *)sq_remfirst(&g_irqchainfreelist);
DEBUGASSERT(node != NULL);
node->handler = g_irqvector[ndx].handler;
node->arg = g_irqvector[ndx].arg;
node->next = NULL;
g_irqvector[ndx].handler = irqchain_dispatch;
g_irqvector[ndx].arg = node;
}
node = (FAR struct irqchain_s *)sq_remfirst(&g_irqchainfreelist);
if (node == NULL)
{
return -ENOMEM;
}
node->handler = isr;
node->arg = arg;
node->next = NULL;
curr = g_irqvector[ndx].arg;
while (curr->next != NULL)
{
curr = curr->next;
}
curr->next = node;
}
else
{
irqchain_detach_all(ndx);
}
return OK;
}
int irqchain_detach(int irq, xcpt_t isr, FAR void *arg)
{
#if NR_IRQS > 0
FAR struct irqchain_s *prev;
FAR struct irqchain_s *curr;
FAR struct irqchain_s *first;
int ret = -EINVAL;
if ((unsigned)irq < NR_IRQS)
{
int ndx = IRQ_TO_NDX(irq);
irqstate_t flags;
if (ndx < 0)
{
return ndx;
}
flags = spin_lock_irqsave(NULL);
if (g_irqvector[ndx].handler == irqchain_dispatch)
{
first = g_irqvector[ndx].arg;
for (prev = NULL, curr = first;
curr != NULL;
prev = curr, curr = curr->next)
{
if (curr->handler == isr && curr->arg == arg)
{
if (curr == first)
{
g_irqvector[ndx].arg = curr->next;
}
else if (curr->next == NULL)
{
prev->next = NULL;
}
else
{
prev->next = curr->next;
}
sq_addlast((FAR struct sq_entry_s *)curr,
&g_irqchainfreelist);
first = g_irqvector[ndx].arg;
if (first->next == NULL)
{
g_irqvector[ndx].handler = first->handler;
g_irqvector[ndx].arg = first->arg;
sq_addlast((FAR struct sq_entry_s *)first,
&g_irqchainfreelist);
}
ret = OK;
break;
}
}
}
else
{
ret = irq_detach(irq);
}
spin_unlock_irqrestore(NULL, flags);
}
return ret;
#else
return OK;
#endif
}