2002-09-26 05:01:54 +04:00
|
|
|
/*
|
2005-03-19 04:58:05 +03:00
|
|
|
* Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
|
|
|
* Distributed under the terms of the NewOS License.
|
|
|
|
*/
|
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
|
|
|
|
#include <int.h>
|
|
|
|
#include <smp.h>
|
2005-03-19 04:58:05 +03:00
|
|
|
#include <util/kqueue.h>
|
2003-10-08 03:12:37 +04:00
|
|
|
#include <boot/kernel_args.h>
|
2005-03-19 04:58:05 +03:00
|
|
|
#include <arch/int.h>
|
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
2005-03-19 04:58:05 +03:00
|
|
|
#include <malloc.h>
|
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
//#define TRACE_INT
|
|
|
|
#ifdef TRACE_INT
|
|
|
|
# define TRACE(x) dprintf x
|
|
|
|
#else
|
|
|
|
# define TRACE(x) ;
|
|
|
|
#endif
|
2002-09-26 05:01:54 +04:00
|
|
|
|
2005-11-04 00:03:06 +03:00
|
|
|
#define DEBUG_INT
|
2002-09-26 05:01:54 +04:00
|
|
|
|
|
|
|
struct io_handler {
|
2002-10-29 06:42:40 +03:00
|
|
|
struct io_handler *next;
|
|
|
|
struct io_handler *prev;
|
|
|
|
interrupt_handler func;
|
|
|
|
void *data;
|
2005-04-27 05:08:35 +04:00
|
|
|
bool use_enable_counter;
|
2002-09-26 05:01:54 +04:00
|
|
|
};
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
struct io_vector {
|
2002-10-29 06:42:40 +03:00
|
|
|
struct io_handler handler_list;
|
|
|
|
spinlock vector_lock;
|
2005-04-27 05:08:35 +04:00
|
|
|
int32 enable_count;
|
2005-11-04 00:03:06 +03:00
|
|
|
#ifdef DEBUG_INT
|
|
|
|
int64 handled_count;
|
|
|
|
int64 unhandled_count;
|
|
|
|
#endif
|
2002-09-26 05:01:54 +04:00
|
|
|
};
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
static struct io_vector io_vectors[NUM_IO_VECTORS];
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2005-04-12 10:09:13 +04:00
|
|
|
|
2002-10-26 20:13:36 +04:00
|
|
|
cpu_status
|
2005-04-12 10:09:13 +04:00
|
|
|
disable_interrupts(void)
|
2002-10-26 20:13:36 +04:00
|
|
|
{
|
|
|
|
return arch_int_disable_interrupts();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
restore_interrupts(cpu_status status)
|
|
|
|
{
|
|
|
|
arch_int_restore_interrupts(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
bool
|
|
|
|
interrupts_enabled(void)
|
|
|
|
{
|
|
|
|
return arch_int_are_interrupts_enabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-04 00:03:06 +03:00
|
|
|
#ifdef DEBUG_INT
|
|
|
|
static int
|
|
|
|
dump_int_statistics(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_IO_VECTORS; i++) {
|
|
|
|
if (io_vectors[i].vector_lock == 0
|
|
|
|
&& io_vectors[i].enable_count == 0
|
|
|
|
&& io_vectors[i].handled_count == 0
|
|
|
|
&& io_vectors[i].unhandled_count == 0
|
|
|
|
&& io_vectors[i].handler_list.next == &io_vectors[i].handler_list)
|
|
|
|
continue;
|
|
|
|
kprintf("int %3d, enabled %ld, handled %8lld, unhandled %8lld%s%s\n",
|
|
|
|
i,
|
|
|
|
io_vectors[i].enable_count,
|
|
|
|
io_vectors[i].handled_count,
|
|
|
|
io_vectors[i].unhandled_count,
|
|
|
|
(io_vectors[i].vector_lock != 0) ? ", ACTIVE" : "",
|
|
|
|
(io_vectors[i].handler_list.next == &io_vectors[i].handler_list) ? ", no handler" : "");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2004-10-21 05:45:43 +04:00
|
|
|
status_t
|
|
|
|
int_init(kernel_args *args)
|
2002-09-26 05:01:54 +04:00
|
|
|
{
|
2005-08-02 20:43:30 +04:00
|
|
|
TRACE(("init_int_handlers: entry\n"));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-10-21 05:45:43 +04:00
|
|
|
return arch_int_init(args);
|
2002-09-26 05:01:54 +04:00
|
|
|
}
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2004-10-21 05:45:43 +04:00
|
|
|
status_t
|
|
|
|
int_init_post_vm(kernel_args *args)
|
2002-09-26 05:01:54 +04:00
|
|
|
{
|
2002-11-19 14:53:41 +03:00
|
|
|
int i;
|
2004-10-21 05:45:43 +04:00
|
|
|
|
2002-11-19 14:53:41 +03:00
|
|
|
/* initialize the vector list */
|
|
|
|
for (i = 0; i < NUM_IO_VECTORS; i++) {
|
|
|
|
io_vectors[i].vector_lock = 0; /* initialize spinlock */
|
2005-04-27 05:08:35 +04:00
|
|
|
io_vectors[i].enable_count = 0;
|
2005-11-04 00:03:06 +03:00
|
|
|
#ifdef DEBUG_INT
|
|
|
|
io_vectors[i].handled_count = 0;
|
|
|
|
io_vectors[i].unhandled_count = 0;
|
|
|
|
#endif
|
2002-11-19 14:53:41 +03:00
|
|
|
initque(&io_vectors[i].handler_list); /* initialize handler queue */
|
|
|
|
}
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-11-04 00:03:06 +03:00
|
|
|
#ifdef DEBUG_INT
|
|
|
|
add_debugger_command("ints", &dump_int_statistics, "list interrupt statistics");
|
|
|
|
#endif
|
|
|
|
|
2004-10-21 05:45:43 +04:00
|
|
|
return arch_int_init_post_vm(args);
|
2002-09-26 05:01:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
/** Install a handler to be called when an interrupt is triggered
|
|
|
|
* for the given interrupt number with \a data as the argument.
|
2002-09-26 05:01:54 +04:00
|
|
|
*/
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-04-12 10:09:13 +04:00
|
|
|
status_t
|
2005-04-27 05:08:35 +04:00
|
|
|
install_io_interrupt_handler(long vector, interrupt_handler handler, void *data, ulong flags)
|
2002-07-19 22:33:41 +04:00
|
|
|
{
|
2002-07-19 20:07:36 +04:00
|
|
|
struct io_handler *io = NULL;
|
2005-04-12 10:09:13 +04:00
|
|
|
cpu_status state;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
if (vector < 0 || vector >= NUM_IO_VECTORS)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2002-10-30 02:07:06 +03:00
|
|
|
io = (struct io_handler *)malloc(sizeof(struct io_handler));
|
2002-09-26 05:01:54 +04:00
|
|
|
if (io == NULL)
|
2005-04-12 10:09:13 +04:00
|
|
|
return B_NO_MEMORY;
|
2002-10-30 02:07:06 +03:00
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
io->func = handler;
|
|
|
|
io->data = data;
|
2005-04-27 05:08:35 +04:00
|
|
|
io->use_enable_counter = (flags & B_NO_ENABLE_COUNTER) == 0;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
// Disable the interrupts, get the spinlock for this irq only
|
|
|
|
// and then insert the handler
|
2002-07-25 05:05:51 +04:00
|
|
|
state = disable_interrupts();
|
2002-09-26 05:01:54 +04:00
|
|
|
acquire_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
insque(io, &io_vectors[vector].handler_list);
|
2002-09-26 05:01:54 +04:00
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
// If B_NO_ENABLE_COUNTER is set, we're being asked to not alter
|
|
|
|
// whether the interrupt should be enabled or not
|
|
|
|
if (io->use_enable_counter) {
|
|
|
|
if (io_vectors[vector].enable_count++ == 0)
|
|
|
|
arch_int_enable_io_interrupt(vector);
|
|
|
|
}
|
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
restore_interrupts(state);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-04-12 10:09:13 +04:00
|
|
|
return B_OK;
|
2002-07-19 22:33:41 +04:00
|
|
|
}
|
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
/** Remove a previously installed interrupt handler */
|
2002-09-26 05:01:54 +04:00
|
|
|
|
2005-04-12 10:09:13 +04:00
|
|
|
status_t
|
2005-04-27 05:08:35 +04:00
|
|
|
remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
|
2002-09-26 05:01:54 +04:00
|
|
|
{
|
2005-04-12 10:09:13 +04:00
|
|
|
status_t status = B_BAD_VALUE;
|
2005-04-27 05:08:35 +04:00
|
|
|
struct io_handler *io = NULL;
|
|
|
|
cpu_status state;
|
2002-09-26 05:01:54 +04:00
|
|
|
|
|
|
|
if (vector < 0 || vector >= NUM_IO_VECTORS)
|
|
|
|
return B_BAD_VALUE;
|
|
|
|
|
2002-07-19 22:33:41 +04:00
|
|
|
/* lock the structures down so it is not modified while we search */
|
2002-09-26 05:01:54 +04:00
|
|
|
state = disable_interrupts();
|
|
|
|
acquire_spinlock(&io_vectors[vector].vector_lock);
|
2002-07-19 20:07:36 +04:00
|
|
|
|
|
|
|
/* loop through the available handlers and try to find a match.
|
2002-09-26 05:01:54 +04:00
|
|
|
* We go forward through the list but this means we start with the
|
2002-07-19 20:07:36 +04:00
|
|
|
* most recently added handlers.
|
|
|
|
*/
|
2002-07-19 22:33:41 +04:00
|
|
|
for (io = io_vectors[vector].handler_list.next;
|
|
|
|
io != &io_vectors[vector].handler_list;
|
2002-09-26 05:01:54 +04:00
|
|
|
io = io->next) {
|
|
|
|
/* we have to match both function and data */
|
2002-07-19 23:25:37 +04:00
|
|
|
if (io->func == handler && io->data == data) {
|
|
|
|
remque(io);
|
2005-04-27 05:08:35 +04:00
|
|
|
|
|
|
|
// Check if we need to disable the interrupt
|
|
|
|
if (io->use_enable_counter && --io_vectors[vector].enable_count == 0)
|
|
|
|
arch_int_disable_io_interrupt(vector);
|
|
|
|
|
2002-11-17 04:59:31 +03:00
|
|
|
status = B_OK;
|
2002-07-19 23:25:37 +04:00
|
|
|
break;
|
|
|
|
}
|
2002-09-26 05:01:54 +04:00
|
|
|
}
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
restore_interrupts(state);
|
2002-07-18 19:03:13 +04:00
|
|
|
|
2005-04-27 05:08:35 +04:00
|
|
|
// if the handler could be found and removed, we still have to free it
|
2002-11-17 04:59:31 +03:00
|
|
|
if (status == B_OK)
|
|
|
|
free(io);
|
|
|
|
|
|
|
|
return status;
|
2002-07-19 22:33:41 +04:00
|
|
|
}
|
|
|
|
|
2002-09-26 05:01:54 +04:00
|
|
|
|
|
|
|
/** actually process an interrupt via the handlers registered for that
|
|
|
|
* vector (irq)
|
2002-07-19 20:07:36 +04:00
|
|
|
*/
|
2002-09-26 05:01:54 +04:00
|
|
|
|
|
|
|
int
|
|
|
|
int_io_interrupt_handler(int vector)
|
2002-11-17 04:59:31 +03:00
|
|
|
{
|
|
|
|
int status = B_UNHANDLED_INTERRUPT;
|
|
|
|
struct io_handler *io;
|
|
|
|
|
2005-12-13 03:06:52 +03:00
|
|
|
#if 0
|
|
|
|
{
|
|
|
|
static int low[2];
|
|
|
|
static int high[2];
|
|
|
|
static int counter;
|
|
|
|
int32 cpu = smp_get_current_cpu();
|
|
|
|
|
|
|
|
if (vector > 0xf0 - 32)
|
|
|
|
high[cpu]++;
|
|
|
|
else
|
|
|
|
low[cpu]++;
|
|
|
|
dprintf("got vector %d at CPU %ld\n", vector, cpu);
|
|
|
|
//if ((counter++ % 10) == 0)
|
|
|
|
dprintf("ints: %d/%d (high: %d/%d)\n", low[0], low[1], high[0], high[1]);
|
|
|
|
}
|
|
|
|
#endif
|
2002-11-17 04:59:31 +03:00
|
|
|
acquire_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
|
2002-11-19 14:53:41 +03:00
|
|
|
// The list can be empty at this place
|
|
|
|
if (io_vectors[vector].handler_list.next == &io_vectors[vector].handler_list) {
|
2002-11-17 04:59:31 +03:00
|
|
|
dprintf("unhandled io interrupt %d\n", vector);
|
2002-11-17 16:32:05 +03:00
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
2002-11-17 04:59:31 +03:00
|
|
|
return B_UNHANDLED_INTERRUPT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop through the list of handlers.
|
|
|
|
* each handler returns as follows...
|
|
|
|
* - B_UNHANDLED_INTERRUPT, the interrupt wasn't processed by the
|
|
|
|
* fucntion, so try the next available.
|
|
|
|
* - B_HANDLED_INTERRUPT, the interrupt has been handled and no further
|
|
|
|
* attention is required
|
|
|
|
* - B_INVOKE_SCHEDULER, the interrupt has been handled, but the function wants
|
|
|
|
* the scheduler to be invoked
|
|
|
|
*
|
|
|
|
* This is a change of behaviour from newos where every handler registered
|
|
|
|
* be called, even if the interrupt had been "handled" by a previous
|
|
|
|
* function.
|
|
|
|
* The logic now is that if there are no handlers then we return
|
|
|
|
* B_UNHANDLED_INTERRUPT and let the system do as it will.
|
|
|
|
* When we have the first function that claims to have "handled" the
|
|
|
|
* interrupt, by returning B_HANDLED_... or B_INVOKE_SCHEDULER we simply
|
|
|
|
* stop calling further handlers and return the value from that
|
|
|
|
* handler.
|
|
|
|
* This may not be correct but appears to be what BeOS did and seems
|
|
|
|
* right.
|
|
|
|
*
|
|
|
|
* ToDo: we might want to reenable calling all registered handlers depending
|
|
|
|
* on a flag somewhere, so that we can deal with buggy drivers
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (io = io_vectors[vector].handler_list.next;
|
|
|
|
io != &io_vectors[vector].handler_list; // Are we already at the end of the list?
|
|
|
|
io = io->next) {
|
|
|
|
if ((status = io->func(io->data)) != B_UNHANDLED_INTERRUPT)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-11-04 00:03:06 +03:00
|
|
|
#ifdef DEBUG_INT
|
|
|
|
if (status != B_UNHANDLED_INTERRUPT)
|
|
|
|
io_vectors[vector].handled_count++;
|
|
|
|
else
|
|
|
|
io_vectors[vector].unhandled_count++;
|
|
|
|
#endif
|
|
|
|
|
2002-11-17 04:59:31 +03:00
|
|
|
release_spinlock(&io_vectors[vector].vector_lock);
|
|
|
|
|
|
|
|
return status;
|
2002-07-10 19:48:19 +04:00
|
|
|
}
|
|
|
|
|