Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | /*
* 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.
*
* 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
};
/* array of event id used by task IRQ objects */
extern const kevent_t _TaskIrqEvt_objIds[];
/**
*
* @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);
}
/**
*
* @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);
}
int task_irq_wait(kirq_t irq_obj, int32_t timeout)
{
__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, timeout);
}
/**
*
* @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 = _TaskIrqEvt_objIds[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,
uint32_t flags)
{
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;
}
irq_obj_ptr->vector = irq_connect_dynamic(
irq, priority, task_irq_int_handler, (void *)irq_obj_ptr,
flags);
irq_enable(irq);
return irq_obj_ptr->vector;
}
#endif /* (CONFIG_MAX_NUM_TASK_IRQS > 0) */
|