minix/kernel/i8259.c
2005-09-11 16:44:06 +00:00

193 lines
6.4 KiB
C
Executable file

/* This file contains routines for initializing the 8259 interrupt controller:
* put_irq_handler: register an interrupt handler
* rm_irq_handler: deregister an interrupt handler
* intr_handle: handle a hardware interrupt
* intr_init: initialize the interrupt controller(s)
*/
#include "kernel.h"
#include "proc.h"
#include <minix/com.h>
#define ICW1_AT 0x11 /* edge triggered, cascade, need ICW4 */
#define ICW1_PC 0x13 /* edge triggered, no cascade, need ICW4 */
#define ICW1_PS 0x19 /* level triggered, cascade, need ICW4 */
#define ICW4_AT_SLAVE 0x01 /* not SFNM, not buffered, normal EOI, 8086 */
#define ICW4_AT_MASTER 0x05 /* not SFNM, not buffered, normal EOI, 8086 */
#define ICW4_PC_SLAVE 0x09 /* not SFNM, buffered, normal EOI, 8086 */
#define ICW4_PC_MASTER 0x0D /* not SFNM, buffered, normal EOI, 8086 */
#if _WORD_SIZE == 2
typedef _PROTOTYPE( void (*vecaddr_t), (void) );
FORWARD _PROTOTYPE( void set_vec, (int vec_nr, vecaddr_t addr) );
PRIVATE vecaddr_t int_vec[] = {
int00, int01, int02, int03, int04, int05, int06, int07,
};
PRIVATE vecaddr_t irq_vec[] = {
hwint00, hwint01, hwint02, hwint03, hwint04, hwint05, hwint06, hwint07,
hwint08, hwint09, hwint10, hwint11, hwint12, hwint13, hwint14, hwint15,
};
#else
#define set_vec(nr, addr) ((void)0)
#endif
/*===========================================================================*
* intr_init *
*===========================================================================*/
PUBLIC void intr_init(mine)
int mine;
{
/* Initialize the 8259s, finishing with all interrupts disabled. This is
* only done in protected mode, in real mode we don't touch the 8259s, but
* use the BIOS locations instead. The flag "mine" is set if the 8259s are
* to be programmed for MINIX, or to be reset to what the BIOS expects.
*/
int i;
intr_disable();
if (machine.protected) {
/* The AT and newer PS/2 have two interrupt controllers, one master,
* one slaved at IRQ 2. (We don't have to deal with the PC that
* has just one controller, because it must run in real mode.)
*/
outb(INT_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT);
outb(INT_CTLMASK, mine ? IRQ0_VECTOR : BIOS_IRQ0_VEC);
/* ICW2 for master */
outb(INT_CTLMASK, (1 << CASCADE_IRQ)); /* ICW3 tells slaves */
outb(INT_CTLMASK, ICW4_AT_MASTER);
outb(INT_CTLMASK, ~(1 << CASCADE_IRQ)); /* IRQ 0-7 mask */
outb(INT2_CTL, machine.ps_mca ? ICW1_PS : ICW1_AT);
outb(INT2_CTLMASK, mine ? IRQ8_VECTOR : BIOS_IRQ8_VEC);
/* ICW2 for slave */
outb(INT2_CTLMASK, CASCADE_IRQ); /* ICW3 is slave nr */
outb(INT2_CTLMASK, ICW4_AT_SLAVE);
outb(INT2_CTLMASK, ~0); /* IRQ 8-15 mask */
/* Copy the BIOS vectors from the BIOS to the Minix location, so we
* can still make BIOS calls without reprogramming the i8259s.
*/
#if IRQ0_VECTOR != BIOS_IRQ0_VEC
phys_copy(BIOS_VECTOR(0) * 4L, VECTOR(0) * 4L, 8 * 4L);
#endif
#if IRQ8_VECTOR != BIOS_IRQ8_VEC
phys_copy(BIOS_VECTOR(8) * 4L, VECTOR(8) * 4L, 8 * 4L);
#endif
} else {
/* Use the BIOS interrupt vectors in real mode. We only reprogram the
* exceptions here, the interrupt vectors are reprogrammed on demand.
* SYS_VECTOR is the Minix system call for message passing.
*/
for (i = 0; i < 8; i++) set_vec(i, int_vec[i]);
set_vec(SYS_VECTOR, s_call);
}
}
/*===========================================================================*
* put_irq_handler *
*===========================================================================*/
PUBLIC void put_irq_handler(hook, irq, handler)
irq_hook_t *hook;
int irq;
irq_handler_t handler;
{
/* Register an interrupt handler. */
int id;
irq_hook_t **line;
if (irq < 0 || irq >= NR_IRQ_VECTORS)
panic("invalid call to put_irq_handler", irq);
line = &irq_handlers[irq];
id = 1;
while (*line != NULL) {
if (hook == *line) return; /* extra initialization */
line = &(*line)->next;
id <<= 1;
}
if (id == 0) panic("Too many handlers for irq", irq);
hook->next = NULL;
hook->handler = handler;
hook->irq = irq;
hook->id = id;
*line = hook;
irq_use |= 1 << irq;
}
/*===========================================================================*
* rm_irq_handler *
*===========================================================================*/
PUBLIC void rm_irq_handler(hook)
irq_hook_t *hook;
{
/* Unregister an interrupt handler. */
int irq = hook->irq;
int id = hook->id;
irq_hook_t **line;
if (irq < 0 || irq >= NR_IRQ_VECTORS)
panic("invalid call to rm_irq_handler", irq);
line = &irq_handlers[irq];
while (*line != NULL) {
if ((*line)->id == id) {
(*line) = (*line)->next;
if (! irq_handlers[irq]) irq_use &= ~(1 << irq);
return;
}
line = &(*line)->next;
}
/* When the handler is not found, normally return here. */
}
/*===========================================================================*
* intr_handle *
*===========================================================================*/
PUBLIC void intr_handle(hook)
irq_hook_t *hook;
{
/* Call the interrupt handlers for an interrupt with the given hook list.
* The assembly part of the handler has already masked the IRQ, reenabled the
* controller(s) and enabled interrupts.
*/
/* Call list of handlers for an IRQ. */
while (hook != NULL) {
/* For each handler in the list, mark it active by setting its ID bit,
* call the function, and unmark it if the function returns true.
*/
irq_actids[hook->irq] |= hook->id;
if ((*hook->handler)(hook)) irq_actids[hook->irq] &= ~hook->id;
hook = hook->next;
}
/* The assembly code will now disable interrupts, unmask the IRQ if and only
* if all active ID bits are cleared, and restart a process.
*/
}
#if _WORD_SIZE == 2
/*===========================================================================*
* set_vec *
*===========================================================================*/
PRIVATE void set_vec(vec_nr, addr)
int vec_nr; /* which vector */
vecaddr_t addr; /* where to start */
{
/* Set up a real mode interrupt vector. */
u16_t vec[2];
/* Build the vector in the array 'vec'. */
vec[0] = (u16_t) addr;
vec[1] = (u16_t) physb_to_hclick(code_base);
/* Copy the vector into place. */
phys_copy(vir2phys(vec), vec_nr * 4L, 4L);
}
#endif /* _WORD_SIZE == 2 */