diff --git a/include/nuttx/irq.h b/include/nuttx/irq.h index 3866f3e49f647..09fa6438ea553 100644 --- a/include/nuttx/irq.h +++ b/include/nuttx/irq.h @@ -47,6 +47,9 @@ */ # define irq_detach(irq) irq_attach(irq, NULL, NULL) +# define irq_detach_wqueue(irq) irq_attach_wqueue(irq, NULL, NULL, NULL, 0) +# define irq_detach_thread(irq) \ + irq_attach_thread(irq, NULL, NULL, NULL, 0, 0) /* Maximum/minimum values of IRQ integer types */ @@ -194,6 +197,31 @@ int irq_attach(int irq, xcpt_t isr, FAR void *arg); int irq_attach_thread(int irq, xcpt_t isr, xcpt_t isrthread, FAR void *arg, int priority, int stack_size); +/**************************************************************************** + * Name: irq_attach_wqueue + * + * Description: + * Configure the IRQ subsystem so that IRQ number 'irq' is dispatched to + * 'wqueue' + * + * Input Parameters: + * irq - Irq num + * isr - Function to be called when the IRQ occurs, called in interrupt + * context. + * If isr is NULL the default handler is installed(irq_default_handler). + * isrwork - called in thread context, If the isrwork is NULL, + * then the ISR is being detached. + * arg - privdate data + * priority - isrwork pri + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int irq_attach_wqueue(int irq, xcpt_t isr, xcpt_t isrwork, + FAR void *arg, int priority); + #ifdef CONFIG_IRQCHAIN int irqchain_detach(int irq, xcpt_t isr, FAR void *arg); #else diff --git a/sched/Kconfig b/sched/Kconfig index 0bb2f02e4a556..e596f412d66cc 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -340,6 +340,18 @@ config PREALLOC_IRQCHAIN endif # IRQCHAIN +config IRQ_NWORKS + int "Max num of active irq wqueue" + default 8 + ---help--- + The max num of active irq wqueue. + +config IRQ_WORK_STACKSIZE + int "The default stack size for isr wqueue" + default DEFAULT_TASK_STACKSIZE + ---help--- + The default stack size for isr wqueue. + config IRQCOUNT bool default n diff --git a/sched/irq/CMakeLists.txt b/sched/irq/CMakeLists.txt index 6f3e23883b6e2..317caec7fa3c8 100644 --- a/sched/irq/CMakeLists.txt +++ b/sched/irq/CMakeLists.txt @@ -18,8 +18,8 @@ # # ############################################################################## -set(SRCS irq_initialize.c irq_attach.c irq_attach_thread.c irq_dispatch.c - irq_unexpectedisr.c) +set(SRCS irq_initialize.c irq_attach.c irq_attach_thread.c irq_attach_wqueue.c + irq_dispatch.c irq_unexpectedisr.c) if(CONFIG_SPINLOCK) list(APPEND SRCS irq_spinlock.c) diff --git a/sched/irq/Make.defs b/sched/irq/Make.defs index cc8eafee828bc..6eda8cb583d23 100644 --- a/sched/irq/Make.defs +++ b/sched/irq/Make.defs @@ -19,7 +19,7 @@ ############################################################################ CSRCS += irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c -CSRCS += irq_attach_thread.c +CSRCS += irq_attach_thread.c irq_attach_wqueue.c ifeq ($(CONFIG_SPINLOCK),y) CSRCS += irq_spinlock.c diff --git a/sched/irq/irq_attach_thread.c b/sched/irq/irq_attach_thread.c index 7973e68ed59d1..6af7aae611edd 100644 --- a/sched/irq/irq_attach_thread.c +++ b/sched/irq/irq_attach_thread.c @@ -170,7 +170,7 @@ int irq_attach_thread(int irq, xcpt_t isr, xcpt_t isrthread, FAR void *arg, if (isrthread == NULL) { - irq_attach(irq, NULL, arg); + irq_detach(irq); DEBUGASSERT(g_irq_thread_pid[ndx] != 0); kthread_delete(g_irq_thread_pid[ndx]); g_irq_thread_pid[ndx] = 0; diff --git a/sched/irq/irq_attach_wqueue.c b/sched/irq/irq_attach_wqueue.c new file mode 100644 index 0000000000000..cc49288f6fc11 --- /dev/null +++ b/sched/irq/irq_attach_wqueue.c @@ -0,0 +1,219 @@ +/**************************************************************************** + * sched/irq/irq_attach_wqueue.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 + +#include +#include + +#include +#include +#include + +#include "irq/irq.h" +#include "sched/sched.h" + +/**************************************************************************** + * Privte Types + ****************************************************************************/ + +/* This is the type of the list of interrupt handlers, one for each IRQ. + * This type provided all of the information necessary to irq_dispatch to + * transfer control to interrupt handlers after the occurrence of an + * interrupt. + */ + +struct irq_work_info_s +{ + xcpt_t handler; /* Address of the interrupt handler */ + xcpt_t isrwork; /* Address of the interrupt worked handler */ + FAR void *arg; /* The argument provided to the interrupt handler. */ + int irq; /* Irq id */ + struct work_s work; /* Interrupt work to the wq */ + + FAR struct kwork_wqueue_s *wqueue; /* Work queue. */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE +static struct irq_work_info_s +g_irq_work_vector[CONFIG_ARCH_NUSER_INTERRUPTS]; +#else +static struct irq_work_info_s g_irq_work_vector[NR_IRQS]; +#endif + +static mutex_t g_irq_wqueue_lock = NXMUTEX_INITIALIZER; +static FAR struct kwork_wqueue_s *g_irq_wqueue[CONFIG_IRQ_NWORKS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static +inline_function FAR struct kwork_wqueue_s *irq_get_wqueue(int priority) +{ + FAR struct kwork_wqueue_s *queue; + int wqueue_priority; + int i; + + nxmutex_lock(&g_irq_wqueue_lock); + for (i = 0; g_irq_wqueue[i] != NULL && i < CONFIG_IRQ_NWORKS; i++) + { + wqueue_priority = work_queue_priority_wq(g_irq_wqueue[i]); + DEBUGASSERT(wqueue_priority >= SCHED_PRIORITY_MIN && + wqueue_priority <= SCHED_PRIORITY_MAX); + + if (wqueue_priority == priority) + { + nxmutex_unlock(&g_irq_wqueue_lock); + return g_irq_wqueue[i]; + } + } + + DEBUGASSERT(i < CONFIG_IRQ_NWORKS); + + queue = work_queue_create("isrwork", priority, + CONFIG_IRQ_WORK_STACKSIZE, 1); + + g_irq_wqueue[i] = queue; + nxmutex_unlock(&g_irq_wqueue_lock); + return queue; +} + +/* Default interrupt handler for worked interrupts. + * Useful for oneshot interrupts. + */ + +static void irq_work_handler(FAR void *arg) +{ + FAR struct irq_work_info_s *info = arg; + + info->isrwork(info->irq, NULL, info->arg); +} + +static int irq_default_handler(int irq, FAR void *regs, FAR void *arg) +{ + FAR struct irq_work_info_s *info; + int ret = IRQ_WAKE_THREAD; + int ndx = IRQ_TO_NDX(irq); + xcpt_t vector; + + if (ndx < 0) + { + return ndx; + } + + info = &g_irq_work_vector[ndx]; + vector = info->handler; + if (vector) + { + ret = vector(irq, regs, arg); + } + + if (ret == IRQ_WAKE_THREAD) + { + work_queue_wq(info->wqueue, &info->work, irq_work_handler, info, 0); + ret = OK; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: irq_attach_wqueue + * + * Description: + * Configure the IRQ subsystem so that IRQ number 'irq' is dispatched to + * 'wqueue' + * + * Input Parameters: + * irq - Irq num + * isr - Function to be called when the IRQ occurs, called in interrupt + * context. + * If isr is NULL the default handler is installed(irq_default_handler). + * isrwork - called in thread context, If the isrwork is NULL, + * then the ISR is being detached. + * arg - privdate data + * priority - isrwork pri + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int irq_attach_wqueue(int irq, xcpt_t isr, xcpt_t isrwork, + FAR void *arg, int priority) +{ + FAR struct irq_work_info_s *info; + +#if NR_IRQS > 0 + int ndx; + + if ((unsigned)irq >= NR_IRQS) + { + return -EINVAL; + } + + ndx = IRQ_TO_NDX(irq); + if (ndx < 0) + { + return ndx; + } + + /* If the isrwork is NULL, then the ISR is being detached. */ + + info = &g_irq_work_vector[ndx]; + + if (isrwork == NULL) + { + irq_detach(irq); + info->isrwork = NULL; + info->handler = NULL; + info->arg = NULL; + info->wqueue = NULL; + return OK; + } + + info->isrwork = isrwork; + info->handler = isr; + info->arg = arg; + info->irq = irq; + if (info->wqueue == NULL) + { + info->wqueue = irq_get_wqueue(priority); + } + + irq_attach(irq, irq_default_handler, arg); +#endif /* NR_IRQS */ + + return OK; +} +