diff options
Diffstat (limited to 'libddekit/interrupt.c')
-rw-r--r-- | libddekit/interrupt.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/libddekit/interrupt.c b/libddekit/interrupt.c new file mode 100644 index 00000000..940363b1 --- /dev/null +++ b/libddekit/interrupt.c @@ -0,0 +1,288 @@ +/* + * \brief Hardware-interrupt subsystem + * \author Thomas Friebel <tf13@os.inf.tu-dresden.de> + * \author Christian Helmuth <ch12@os.inf.tu-dresden.de> + * \date 2007-01-22 + * + * FIXME could intloop_param freed after startup? + * FIXME use consume flag to indicate IRQ was handled + */ + +#include <stdio.h> +#include <error.h> +#include <mach.h> +#include <hurd.h> + +#include "ddekit/interrupt.h" +#include "ddekit/semaphore.h" +#include "ddekit/printf.h" +#include "ddekit/memory.h" +#include "ddekit/condvar.h" + +#include "device_U.h" + +#define DEBUG_INTERRUPTS 0 + +#define MAX_INTERRUPTS 32 + +#define BLOCK_IRQ 0 + +#define MACH_INTR_NOTIFY 424242 + +typedef struct +{ + mach_msg_header_t intr_header; + mach_msg_type_t intr_type; + int line; +} mach_intr_notification_t; + +/* + * Internal type for interrupt loop parameters + */ +struct intloop_params +{ + unsigned irq; /* irq number */ + int shared; /* irq sharing supported? */ + void(*thread_init)(void *); /* thread initialization */ + void(*handler)(void *); /* IRQ handler function */ + void *priv; /* private token */ + ddekit_sem_t *started; + + int start_err; +}; + +static struct +{ + int handle_irq; /* nested irq disable count */ + ddekit_lock_t irqlock; + struct ddekit_condvar *cond; + ddekit_thread_t *irq_thread; /* thread ID for detaching from IRQ later on */ + boolean_t thread_exit; + thread_t mach_thread; +} ddekit_irq_ctrl[MAX_INTERRUPTS]; + +static mach_port_t master_device; +static mach_port_t master_host; + +/** + * Interrupt service loop + * + */ +static void intloop(void *arg) +{ + kern_return_t ret; + struct intloop_params *params = arg; + mach_port_t delivery_port; + mach_port_t pset, psetcntl; + int my_index; + + ret = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &delivery_port); + if (ret) + error (2, ret, "mach_port_allocate"); + + my_index = params->irq; + ddekit_irq_ctrl[my_index].mach_thread = mach_thread_self (); + ret = thread_get_assignment (mach_thread_self (), &pset); + if (ret) + error (0, ret, "thread_get_assignment"); + ret = host_processor_set_priv (master_host, pset, &psetcntl); + if (ret) + error (0, ret, "host_processor_set_priv"); + thread_max_priority (mach_thread_self (), psetcntl, 0); + ret = thread_priority (mach_thread_self (), DDEKIT_IRQ_PRIO, 0); + if (ret) + error (0, ret, "thread_priority"); + + // TODO the flags for shared irq should be indicated by params->shared. + // Be careful. For now, we must use shared irq. + // Otherwise, the interrupt handler cannot be installed in the kernel. + ret = device_intr_register (master_device, params->irq, + 0, 0x04000000, delivery_port, + MACH_MSG_TYPE_MAKE_SEND); + ddekit_printf ("device_intr_register returns %d\n", ret); + if (ret) { + /* inform thread creator of error */ + /* XXX does omega0 error code have any meaning to DDEKit users? */ + params->start_err = ret; + ddekit_sem_up(params->started); + ddekit_printf ("cannot install irq %d\n", params->irq); + return; + } + device_intr_enable (master_device, params->irq, TRUE); + +#if 0 + /* + * Setup an exit fn. This will make sure that we clean up everything, + * before shutting down an IRQ thread. + */ + if (l4thread_on_exit(&exit_fn, (void *)my_index) < 0) + ddekit_panic("Could not set exit handler for IRQ fn."); +#endif + + /* after successful initialization call thread_init() before doing anything + * else here */ + if (params->thread_init) params->thread_init(params->priv); + + /* save handle + inform thread creator of success */ + params->start_err = 0; + ddekit_sem_up(params->started); + + int irq_server (mach_msg_header_t *inp, mach_msg_header_t *outp) { + mach_intr_notification_t *intr_header = (mach_intr_notification_t *) inp; + + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + if (inp->msgh_id != MACH_INTR_NOTIFY) + return 0; + + /* It's an interrupt not for us. It shouldn't happen. */ + if (intr_header->line != params->irq) { + ddekit_printf ("We get interrupt %d, %d is expected", + intr_header->line, params->irq); + return 1; + } + + /* only call registered handler function, if IRQ is not disabled */ + ddekit_lock_lock (&ddekit_irq_ctrl[my_index].irqlock); + while (ddekit_irq_ctrl[my_index].handle_irq <= 0) { + ddekit_condvar_wait (ddekit_irq_ctrl[my_index].cond, + &ddekit_irq_ctrl[my_index].irqlock); + // TODO if it's edged-triggered irq, the interrupt will be lost. + } + params->handler(params->priv); + /* If the irq has been disabled by the linux device, + * we don't need to reenable the real one. */ + device_intr_enable (master_device, my_index, TRUE); + + if (ddekit_irq_ctrl[my_index].thread_exit) { + ddekit_lock_unlock (&ddekit_irq_ctrl[my_index].irqlock); + ddekit_thread_exit(); + return 1; + } + ddekit_lock_unlock (&ddekit_irq_ctrl[my_index].irqlock); + return 1; + } + + mach_msg_server (irq_server, 0, delivery_port); +} + + +/** + * Attach to hardware interrupt + * + * \param irq IRQ number to attach to + * \param shared set to 1 if interrupt sharing is supported; set to 0 + * otherwise + * \param thread_init called just after DDEKit internal init and before any + * other function + * \param handler IRQ handler for interrupt irq + * \param priv private token (argument for thread_init and handler) + * + * \return pointer to interrupt thread created + */ +ddekit_thread_t *ddekit_interrupt_attach(int irq, int shared, + void(*thread_init)(void *), + void(*handler)(void *), void *priv) +{ + struct intloop_params *params; + ddekit_thread_t *thread; + char thread_name[10]; + + /* We cannot attach the interrupt to the irq which has been used. */ + if (ddekit_irq_ctrl[irq].irq_thread) + return NULL; + + /* initialize info structure for interrupt loop */ + params = ddekit_simple_malloc(sizeof(*params)); + if (!params) return NULL; + + // TODO make sure irq is 0-15 instead of 1-16. + params->irq = irq; + params->thread_init = thread_init; + params->handler = handler; + params->priv = priv; + params->started = ddekit_sem_init(0); + params->start_err = 0; + params->shared = shared; + + /* construct name */ + snprintf(thread_name, 10, "irq%02X", irq); + + ddekit_irq_ctrl[irq].handle_irq = 1; /* IRQ nesting level is initially 1 */ + ddekit_lock_init_unlocked (&ddekit_irq_ctrl[irq].irqlock); + ddekit_irq_ctrl[irq].cond = ddekit_condvar_init (); + ddekit_irq_ctrl[irq].thread_exit = FALSE; + + /* allocate irq */ + /* create interrupt loop thread */ + thread = ddekit_thread_create(intloop, params, thread_name); + if (!thread) { + ddekit_simple_free(params); + return NULL; + } + ddekit_irq_ctrl[irq].irq_thread = thread; + + + /* wait for intloop initialization result */ + ddekit_sem_down(params->started); + ddekit_sem_deinit(params->started); + if (params->start_err) { + ddekit_simple_free(params); + return NULL; + } + + return thread; +} + +/** + * Detach from interrupt by disabling it and then shutting down the IRQ + * thread. + */ +void ddekit_interrupt_detach(int irq) +{ + ddekit_interrupt_disable(irq); + // TODO the code should be removed. + ddekit_lock_lock (&ddekit_irq_ctrl[irq].irqlock); + if (ddekit_irq_ctrl[irq].handle_irq == 0) { + ddekit_irq_ctrl[irq].thread_exit = TRUE; + ddekit_irq_ctrl[irq].irq_thread = NULL; + + /* If the irq thread is waiting for interrupt notification + * messages, thread_abort() can force it to return. + * I hope this ugly trick can work. */ + thread_abort (ddekit_irq_ctrl[irq].mach_thread); + } + ddekit_lock_unlock (&ddekit_irq_ctrl[irq].irqlock); +} + + +void ddekit_interrupt_disable(int irq) +{ + if (ddekit_irq_ctrl[irq].irqlock) { + ddekit_lock_lock (&ddekit_irq_ctrl[irq].irqlock); + --ddekit_irq_ctrl[irq].handle_irq; + ddekit_lock_unlock (&ddekit_irq_ctrl[irq].irqlock); + } +} + + +void ddekit_interrupt_enable(int irq) +{ + if (ddekit_irq_ctrl[irq].irqlock) { + ddekit_lock_lock (&ddekit_irq_ctrl[irq].irqlock); + ++ddekit_irq_ctrl[irq].handle_irq; + if (ddekit_irq_ctrl[irq].handle_irq > 0) + ddekit_condvar_signal (ddekit_irq_ctrl[irq].cond); + ddekit_lock_unlock (&ddekit_irq_ctrl[irq].irqlock); + } +} + +void interrupt_init () +{ + + error_t err; + + err = get_privileged_ports (&master_host, &master_device); + if (err) + error (1, err, "get_privileged_ports"); +} |