zephyr/kernel/microkernel/k_irq.c

240 lines
7.0 KiB
C

/*
* Copyright (c) 2013-2014 Wind River Systems, Inc.
*
* Licensed 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.
*/
/**
* @brief Task IRQ kernel services
*
* 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 task_id; /* requesting task */
};
/* task IRQ object type */
struct task_irq_info {
ktask_t task_id; /* 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)].task_id = 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.
*
* @param parameter Pointer to task IRQ object
*
* @return N/A
*/
static void task_irq_int_handler(void *parameter)
{
struct task_irq_info *irq_obj_ptr = parameter;
isr_event_send(irq_obj_ptr->event);
irq_disable(irq_obj_ptr->irq);
}
void task_irq_free(kirq_t irq_obj)
{
__ASSERT(irq_obj < MAX_TASK_IRQS, "Invalid IRQ object");
__ASSERT(task_irq_object[irq_obj].task_id == 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].task_id = INVALID_TASK;
}
/**
*
* @brief Re-enable a task IRQ object's interrupt
*
* This re-enables the interrupt for a task IRQ object.
* @param irq_obj IRQ object identifier
*
* @return N/A
*/
void task_irq_ack(kirq_t irq_obj)
{
__ASSERT(irq_obj < MAX_TASK_IRQS, "Invalid IRQ object");
__ASSERT(task_irq_object[irq_obj].task_id == 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 signaled an interrupt.
* @param irq_obj IRQ object identifier
* @param time Time to wait (in ticks)
*
* @return RC_OK, RC_FAIL, or RC_TIME
*/
int _task_irq_test(kirq_t irq_obj, int32_t time)
{
__ASSERT(irq_obj < MAX_TASK_IRQS, "Invalid IRQ object");
__ASSERT(task_irq_object[irq_obj].task_id == 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.
* @param arg Pointer to registration request arguments
*
* @return ptr to allocated task IRQ object if successful, NULL if not
*/
static int _k_task_irq_alloc(void *arg)
{
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].task_id != 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].task_id != INVALID_TASK)) {
return (int)NULL;
}
}
/* Take ownership of specified IRQ object */
irq_obj_ptr = &task_irq_object[argp->irq_obj];
irq_obj_ptr->task_id = argp->task_id;
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;
}
uint32_t task_irq_alloc(kirq_t irq_obj, uint32_t irq, uint32_t 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.task_id = 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) */