| /* |
| * 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) */ |