diff --git a/include/nuttx/irq.h b/include/nuttx/irq.h index 0fa8867629966..3866f3e49f647 100644 --- a/include/nuttx/irq.h +++ b/include/nuttx/irq.h @@ -82,6 +82,14 @@ while (0) #endif +/* Interrupt was handled by this device */ + +#define IRQ_HANDLED 0 + +/* Handler requests to wake the handler thread */ + +#define IRQ_WAKE_THREAD 1 + /**************************************************************************** * Public Types ****************************************************************************/ @@ -160,6 +168,32 @@ extern "C" int irq_attach(int irq, xcpt_t isr, FAR void *arg); +/**************************************************************************** + * Name: irq_attach_thread + * + * Description: + * Configure the IRQ subsystem so that IRQ number 'irq' is dispatched to + * 'isrthread' + * + * 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). + * isrthread - called in thread context, If the isrthread is NULL, + * then the ISR is being detached. + * arg - privdate data + * priority - Priority of the new task + * stack_size - size (in bytes) of the stack needed + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int irq_attach_thread(int irq, xcpt_t isr, xcpt_t isrthread, FAR void *arg, + int priority, int stack_size); + #ifdef CONFIG_IRQCHAIN int irqchain_detach(int irq, xcpt_t isr, FAR void *arg); #else diff --git a/sched/irq/CMakeLists.txt b/sched/irq/CMakeLists.txt index b569005c34019..6f3e23883b6e2 100644 --- a/sched/irq/CMakeLists.txt +++ b/sched/irq/CMakeLists.txt @@ -18,7 +18,8 @@ # # ############################################################################## -set(SRCS irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c) +set(SRCS irq_initialize.c irq_attach.c irq_attach_thread.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 926fe4caf8adb..cc8eafee828bc 100644 --- a/sched/irq/Make.defs +++ b/sched/irq/Make.defs @@ -19,6 +19,7 @@ ############################################################################ CSRCS += irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c +CSRCS += irq_attach_thread.c ifeq ($(CONFIG_SPINLOCK),y) CSRCS += irq_spinlock.c diff --git a/sched/irq/irq.h b/sched/irq/irq.h index b1be90ea85069..1bf1146ac98cc 100644 --- a/sched/irq/irq.h +++ b/sched/irq/irq.h @@ -44,6 +44,13 @@ # error CONFIG_ARCH_NUSER_INTERRUPTS is not defined #endif +#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE +# define IRQ_TO_NDX(irq) \ + (g_irqmap[(irq)] < CONFIG_ARCH_NUSER_INTERRUPTS ? g_irqmap[(irq)] : -EINVAL) +#else +# define IRQ_TO_NDX(irq) (irq) +#endif + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/sched/irq/irq_attach.c b/sched/irq/irq_attach.c index 83249c4a2ef97..441861413a0ad 100644 --- a/sched/irq/irq_attach.c +++ b/sched/irq/irq_attach.c @@ -50,22 +50,13 @@ int irq_attach(int irq, xcpt_t isr, FAR void *arg) if ((unsigned)irq < NR_IRQS) { + int ndx = IRQ_TO_NDX(irq); irqstate_t flags; - int ndx; -#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE - /* Is there a mapping for this IRQ number? */ - - ndx = g_irqmap[irq]; - if ((unsigned)ndx >= CONFIG_ARCH_NUSER_INTERRUPTS) + if (ndx < 0) { - /* No.. then return failure. */ - - return ret; + return ndx; } -#else - ndx = irq; -#endif /* If the new ISR is NULL, then the ISR is being detached. * In this case, disable the ISR and direct any interrupts diff --git a/sched/irq/irq_attach_thread.c b/sched/irq/irq_attach_thread.c new file mode 100644 index 0000000000000..7973e68ed59d1 --- /dev/null +++ b/sched/irq/irq_attach_thread.c @@ -0,0 +1,208 @@ +/**************************************************************************** + * sched/irq/irq_attach_thread.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_thread_info_s +{ + xcpt_t handler; /* Address of the interrupt handler */ + FAR void *arg; /* The argument provided to the interrupt handler. */ + FAR sem_t *sem; /* irq sem used to notify irq thread */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static pid_t g_irq_thread_pid[NR_IRQS]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Default interrupt handler for threaded interrupts. + * Useful for oneshot interrupts. + */ + +static int irq_default_handler(int irq, FAR void *regs, FAR void *arg) +{ + FAR struct irq_thread_info_s *info = arg; + int ret = IRQ_WAKE_THREAD; + + DEBUGASSERT(info->handler != NULL); + ret = info->handler(irq, regs, info->arg); + + if (ret == IRQ_WAKE_THREAD) + { + nxsem_post(info->sem); + ret = OK; + } + + return ret; +} + +static int isr_thread_main(int argc, FAR char *argv[]) +{ + int irq = atoi(argv[1]); + xcpt_t isr = (xcpt_t)((uintptr_t)strtoul(argv[2], NULL, 16)); + xcpt_t isrthread = (xcpt_t)((uintptr_t)strtoul(argv[3], NULL, 16)); + FAR void *arg = (FAR void *)((uintptr_t)strtoul(argv[4], NULL, 16)); + struct irq_thread_info_s info; + sem_t sem; + + info.sem = &sem; + info.arg = arg; + info.handler = isr; + + nxsem_init(&sem, 0, 0); + + irq_attach(irq, irq_default_handler, &info); + +#if !defined(CONFIG_ARCH_NOINTC) + up_enable_irq(irq); +#endif + + for (; ; ) + { + if (nxsem_wait_uninterruptible(&sem) < 0) + { + continue; + } + + isrthread(irq, NULL, arg); + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: irq_attach_thread + * + * Description: + * Configure the IRQ subsystem so that IRQ number 'irq' is dispatched to + * 'isrthread' and up_enable_irq will be invoked after isrthread started. + * + * 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). + * isrthread - called in thread context, If the isrthread is NULL, + * then the ISR is being detached. + * arg - privdate data + * priority - Priority of the new task + * stack_size - size (in bytes) of the stack needed + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +int irq_attach_thread(int irq, xcpt_t isr, xcpt_t isrthread, FAR void *arg, + int priority, int stack_size) +{ +#if NR_IRQS > 0 + FAR char *argv[5]; + char arg1[32]; /* irq */ + char arg2[32]; /* isr */ + char arg3[32]; /* isrthread */ + char arg4[32]; /* arg */ + pid_t pid; + int ndx; + + if ((unsigned)irq >= NR_IRQS) + { + return -EINVAL; + } + + ndx = IRQ_TO_NDX(irq); + if (ndx < 0) + { + return ndx; + } + + /* If the isrthread is NULL, then the ISR is being detached. */ + + if (isrthread == NULL) + { + irq_attach(irq, NULL, arg); + DEBUGASSERT(g_irq_thread_pid[ndx] != 0); + kthread_delete(g_irq_thread_pid[ndx]); + g_irq_thread_pid[ndx] = 0; + + return OK; + } + + if (g_irq_thread_pid[ndx] != 0) + { + return -EINVAL; + } + + snprintf(arg1, sizeof(arg1), "%d", irq); + snprintf(arg2, sizeof(arg2), "%p", isr); + snprintf(arg3, sizeof(arg3), "%p", isrthread); + snprintf(arg4, sizeof(arg4), "%p", arg); + argv[0] = arg1; + argv[1] = arg2; + argv[2] = arg3; + argv[3] = arg4; + argv[4] = NULL; + + pid = kthread_create("isr_thread", priority, stack_size, + isr_thread_main, argv); + if (pid < 0) + { + return pid; + } + + g_irq_thread_pid[ndx] = pid; + +#endif /* NR_IRQS */ + + return OK; +} diff --git a/sched/irq/irq_chain.c b/sched/irq/irq_chain.c index f5bd0736c98d3..648ee83947bad 100644 --- a/sched/irq/irq_chain.c +++ b/sched/irq/irq_chain.c @@ -201,22 +201,13 @@ int irqchain_detach(int irq, xcpt_t isr, FAR void *arg) if ((unsigned)irq < NR_IRQS) { + int ndx = IRQ_TO_NDX(irq); irqstate_t flags; - int ndx; -#ifdef CONFIG_ARCH_MINIMAL_VECTORTABLE - /* Is there a mapping for this IRQ number? */ - - ndx = g_irqmap[irq]; - if ((unsigned)ndx >= CONFIG_ARCH_NUSER_INTERRUPTS) + if (ndx < 0) { - /* No.. then return failure. */ - - return ret; + return ndx; } -#else - ndx = irq; -#endif flags = spin_lock_irqsave(NULL);