287 lines
8.6 KiB
C
287 lines
8.6 KiB
C
/* task IRQ kernel services */
|
|
|
|
/*
|
|
* Copyright (c) 2013-2014 Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This module manages the interrupt functionality between a task level device
|
|
driver and the kernel.
|
|
|
|
A task level device driver allocates a specific task IRQ object by providing
|
|
an IRQ and priority level; the registration process allocates an interrupt
|
|
vector, sets up an ISR, and enables the associated interrupt. When an
|
|
interrupt occurs, the ISR handler signals an event specific to the IRQ object.
|
|
The task level device driver tests/waits on this event number to determine
|
|
if/when the interrupt has occurred. As the ISR also disables the interrupt, the
|
|
task level device driver subsequently make a request to have the interrupt
|
|
enabled again. If desired, the device driver can free an IRQ object that
|
|
it is no longer interested in using.
|
|
|
|
These routines perform error checking to ensure that an IRQ object can only be
|
|
allocated by a single task, and that subsequent operations on that IRQ object
|
|
are only performed by that task. This checking is necessary to ensure that
|
|
a task cannot impact the operation of an IRQ object it does not own.
|
|
*/
|
|
|
|
#if (CONFIG_MAX_NUM_TASK_IRQS > 0)
|
|
|
|
#include <nano_private.h>
|
|
#include <microkernel.h>
|
|
#include <micro_private.h>
|
|
#include <misc/__assert.h>
|
|
|
|
#define MAX_TASK_IRQS CONFIG_MAX_NUM_TASK_IRQS
|
|
|
|
#define INVALID_TASK -1
|
|
|
|
/* task IRQ object registration request */
|
|
|
|
struct irq_obj_reg_arg {
|
|
kirq_t irq_obj; /* IRQ object identifier */
|
|
uint32_t irq; /* IRQ of device */
|
|
ktask_t taskId; /* requesting task */
|
|
};
|
|
|
|
/* task IRQ object type */
|
|
|
|
struct task_irq_info {
|
|
ktask_t taskId; /* task ID of task IRQ object's owner */
|
|
uint32_t irq; /* IRQ used by task IRQ object */
|
|
kevent_t event; /* event number assigned to task IRQ object */
|
|
uint32_t vector; /* interrupt vector assigned to task IRQ object */
|
|
};
|
|
|
|
/* task IRQ object array */
|
|
|
|
static struct task_irq_info task_irq_object[MAX_TASK_IRQS] = {
|
|
[0 ...(MAX_TASK_IRQS - 1)].taskId = INVALID_TASK
|
|
};
|
|
|
|
/* architecture-specific */
|
|
|
|
#if defined(CONFIG_X86_32)
|
|
|
|
#define RELEASE_VECTOR(v) _IntVecMarkFree(v)
|
|
|
|
#elif defined(CONFIG_CPU_CORTEX_M3_M4)
|
|
#include <arch/cpu.h>
|
|
extern void _irq_disconnect(unsigned int irq);
|
|
#define RELEASE_VECTOR(v) _irq_disconnect(v)
|
|
#else
|
|
#error "Unknown target"
|
|
#endif
|
|
|
|
/* event id used by first task IRQ object */
|
|
|
|
extern const kevent_t _TaskIrqEvt0_objId;
|
|
|
|
/**
|
|
*
|
|
* @brief ISR for task IRQ objects
|
|
*
|
|
* This ISR handles interrupts generated by registered task IRQ objects.
|
|
*
|
|
* The ISR triggers an event signal specified by the event number associated
|
|
* with a particular task IRQ object; the interrupt for the task IRQ object
|
|
* is then disabled. The parameter provided to the ISR is a structure that
|
|
* contains information about the objects's vector, IRQ, and event number.
|
|
*
|
|
* This ISR does not facilitate an int acknowledgment as it presumes that an
|
|
* End of Interrupt (EOI) routine is provided by the interrupt controller that
|
|
* is being used.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
static void task_irq_int_handler(
|
|
void *parameter /* ptr to task IRQ object */
|
|
)
|
|
{
|
|
struct task_irq_info *irq_obj_ptr = parameter;
|
|
|
|
isr_event_send(irq_obj_ptr->event);
|
|
irq_disable(irq_obj_ptr->irq);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Free a task IRQ object
|
|
*
|
|
* The task IRQ object's interrupt is disabled, and the associated event
|
|
* is flushed; the object's interrupt vector is then freed, and the object's
|
|
* global array entry is marked as unused.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void task_irq_free(kirq_t irq_obj /* IRQ object identifier */
|
|
)
|
|
{
|
|
__ASSERT(irq_obj < MAX_TASK_IRQS, "Invalid IRQ object");
|
|
__ASSERT(task_irq_object[irq_obj].taskId == task_id_get(),
|
|
"Incorrect Task ID");
|
|
|
|
irq_disable(task_irq_object[irq_obj].irq);
|
|
RELEASE_VECTOR(task_irq_object[irq_obj].vector);
|
|
(void)task_event_recv(task_irq_object[irq_obj].event);
|
|
task_irq_object[irq_obj].taskId = INVALID_TASK;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Re-enable a task IRQ object's interrupt
|
|
*
|
|
* This re-enables the interrupt for a task IRQ object.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void task_irq_ack(kirq_t irq_obj /* IRQ object identifier */
|
|
)
|
|
{
|
|
__ASSERT(irq_obj < MAX_TASK_IRQS, "Invalid IRQ object");
|
|
__ASSERT(task_irq_object[irq_obj].taskId == task_id_get(),
|
|
"Incorrect Task ID");
|
|
|
|
irq_enable(task_irq_object[irq_obj].irq);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Determine if a task IRQ object has had an interrupt
|
|
*
|
|
* This tests a task IRQ object to see if it has signalled an interrupt.
|
|
*
|
|
* @return RC_OK, RC_FAIL, or RC_TIME
|
|
*/
|
|
|
|
int _task_irq_test(kirq_t irq_obj, /* IRQ object identifier */
|
|
int32_t time /* time to wait (in ticks) */
|
|
)
|
|
{
|
|
__ASSERT(irq_obj < MAX_TASK_IRQS, "Invalid IRQ object");
|
|
__ASSERT(task_irq_object[irq_obj].taskId == task_id_get(),
|
|
"Incorrect Task ID");
|
|
|
|
return _task_event_recv(task_irq_object[irq_obj].event, time);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Allocate a task IRQ object
|
|
*
|
|
* This routine allocates a task IRQ object to a task.
|
|
*
|
|
* @return ptr to allocated task IRQ object if successful, NULL if not
|
|
*/
|
|
|
|
static int _k_task_irq_alloc(
|
|
void *arg /* ptr to registration request arguments */
|
|
)
|
|
{
|
|
struct irq_obj_reg_arg *argp = (struct irq_obj_reg_arg *)arg;
|
|
struct task_irq_info *irq_obj_ptr; /* ptr to task IRQ object */
|
|
int curr_irq_obj; /* IRQ object loop counter */
|
|
|
|
/* Fail if the requested IRQ object is already in use */
|
|
|
|
if (task_irq_object[argp->irq_obj].taskId != INVALID_TASK) {
|
|
return (int)NULL;
|
|
}
|
|
|
|
/* Fail if the requested IRQ is already in use */
|
|
|
|
for (curr_irq_obj = 0; curr_irq_obj < MAX_TASK_IRQS; curr_irq_obj++) {
|
|
if ((task_irq_object[curr_irq_obj].irq == argp->irq) &&
|
|
(task_irq_object[curr_irq_obj].taskId != INVALID_TASK)) {
|
|
return (int)NULL;
|
|
}
|
|
}
|
|
|
|
/* Take ownership of specified IRQ object */
|
|
|
|
irq_obj_ptr = &task_irq_object[argp->irq_obj];
|
|
irq_obj_ptr->taskId = argp->taskId;
|
|
irq_obj_ptr->irq = argp->irq;
|
|
irq_obj_ptr->event = (_TaskIrqEvt0_objId + argp->irq_obj);
|
|
irq_obj_ptr->vector = INVALID_VECTOR;
|
|
|
|
return (int)irq_obj_ptr;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief Register a task IRQ object
|
|
*
|
|
* This routine connects a task IRQ object to a system interrupt based
|
|
* upon the specified IRQ and priority values.
|
|
*
|
|
* IRQ allocation is done via the microkernel server fiber so that simultaneous
|
|
* allocation requests are single-threaded.
|
|
*
|
|
* @return assigned interrupt vector if successful, INVALID_VECTOR if not
|
|
*/
|
|
|
|
uint32_t task_irq_alloc(
|
|
kirq_t irq_obj, /* IRQ object identifier */
|
|
uint32_t irq, /* requested IRQ */
|
|
uint32_t priority /* requested interrupt priority */
|
|
)
|
|
{
|
|
struct irq_obj_reg_arg arg; /* IRQ object registration request arguments */
|
|
struct task_irq_info *irq_obj_ptr; /* ptr to task IRQ object */
|
|
|
|
/* Allocate the desired IRQ object and IRQ */
|
|
|
|
arg.irq_obj = irq_obj;
|
|
arg.irq = irq;
|
|
arg.taskId = task_id_get();
|
|
|
|
irq_obj_ptr = (struct task_irq_info *)task_offload_to_fiber(_k_task_irq_alloc,
|
|
(void *)&arg);
|
|
if (irq_obj_ptr == NULL) {
|
|
return INVALID_VECTOR;
|
|
}
|
|
|
|
/*
|
|
* NOTE: the comma that seems to be missing is part of the IRQ_STUB
|
|
* definition to abstract the different irq_connect signatures
|
|
*/
|
|
irq_obj_ptr->vector = irq_connect(
|
|
irq, priority, task_irq_int_handler, (void *)irq_obj_ptr);
|
|
irq_enable(irq);
|
|
|
|
return irq_obj_ptr->vector;
|
|
}
|
|
|
|
|
|
#endif /* (CONFIG_MAX_NUM_TASK_IRQS > 0) */
|