cf9a4ec79b
Coverity was flagging a recursive include between kernel.h and cpulocals.h. As cpulocals.h also included proc.h, we can move that include statement into kernel.h, and clean up the source files' include statements accordingly.
178 lines
4.8 KiB
C
178 lines
4.8 KiB
C
/*
|
|
* The Minix hardware interrupt system.
|
|
*
|
|
* This file contains routines for managing the interrupt
|
|
* controller.
|
|
*
|
|
* put_irq_handler: register an interrupt handler.
|
|
* rm_irq_handler: deregister an interrupt handler.
|
|
* irq_handle: handle a hardware interrupt.
|
|
* called by the system dependent part when an
|
|
* external interrupt occures.
|
|
* enable_irq: enable hook for IRQ.
|
|
* disable_irq: disable hook for IRQ.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "kernel.h"
|
|
#include "archconst.h"
|
|
#include "hw_intr.h"
|
|
|
|
|
|
/* number of lists of IRQ hooks, one list per supported line. */
|
|
static irq_hook_t* irq_handlers[NR_IRQ_VECTORS] = {0};
|
|
|
|
/*===========================================================================*
|
|
* put_irq_handler *
|
|
*===========================================================================*/
|
|
/* Register an interrupt handler. */
|
|
void put_irq_handler( irq_hook_t* hook, int irq,
|
|
const irq_handler_t handler)
|
|
{
|
|
int id;
|
|
irq_hook_t **line;
|
|
unsigned long bitmap;
|
|
|
|
if( irq < 0 || irq >= NR_IRQ_VECTORS )
|
|
panic("invalid call to put_irq_handler: %d", irq);
|
|
|
|
line = &irq_handlers[irq];
|
|
|
|
bitmap = 0;
|
|
while ( *line != NULL ) {
|
|
if(hook == *line) return; /* extra initialization */
|
|
bitmap |= (*line)->id; /* mark ids in use */
|
|
line = &(*line)->next;
|
|
}
|
|
|
|
/* find the lowest id not in use */
|
|
for (id = 1; id != 0; id <<= 1)
|
|
if (!(bitmap & id)) break;
|
|
|
|
if(id == 0)
|
|
panic("Too many handlers for irq: %d", irq);
|
|
|
|
hook->next = NULL;
|
|
hook->handler = handler;
|
|
hook->irq = irq;
|
|
hook->id = id;
|
|
*line = hook;
|
|
|
|
/* And as last enable the irq at the hardware.
|
|
*
|
|
* Internal this activates the line or source of the given interrupt.
|
|
*/
|
|
if((irq_actids[hook->irq] &= ~hook->id) == 0) {
|
|
hw_intr_used(irq);
|
|
hw_intr_unmask(hook->irq);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* rm_irq_handler *
|
|
*===========================================================================*/
|
|
/* Unregister an interrupt handler. */
|
|
void rm_irq_handler( const irq_hook_t* hook ) {
|
|
const int irq = hook->irq;
|
|
const int id = hook->id;
|
|
irq_hook_t **line;
|
|
|
|
if( irq < 0 || irq >= NR_IRQ_VECTORS )
|
|
panic("invalid call to rm_irq_handler: %d", irq);
|
|
|
|
/* remove the hook */
|
|
line = &irq_handlers[irq];
|
|
while( (*line) != NULL ) {
|
|
if((*line)->id == id) {
|
|
(*line) = (*line)->next;
|
|
if (irq_actids[irq] & id)
|
|
irq_actids[irq] &= ~id;
|
|
}
|
|
else {
|
|
line = &(*line)->next;
|
|
}
|
|
}
|
|
|
|
/* Disable the irq if there are no other handlers registered.
|
|
* If the irq is shared, reenable it if there is no active handler.
|
|
*/
|
|
if (irq_handlers[irq] == NULL) {
|
|
hw_intr_mask(irq);
|
|
hw_intr_not_used(irq);
|
|
}
|
|
else if (irq_actids[irq] == 0) {
|
|
hw_intr_unmask(irq);
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* irq_handle *
|
|
*===========================================================================*/
|
|
/*
|
|
* The function first disables interrupt is need be and restores the state at
|
|
* the end. Before returning, it unmasks the IRQ if and only if all active ID
|
|
* bits are cleared, and restart a process.
|
|
*/
|
|
void irq_handle(int irq)
|
|
{
|
|
irq_hook_t * hook;
|
|
|
|
/* here we need not to get this IRQ until all the handlers had a say */
|
|
assert(irq >= 0 && irq < NR_IRQ_VECTORS);
|
|
hw_intr_mask(irq);
|
|
hook = irq_handlers[irq];
|
|
|
|
/* Check for spurious interrupts. */
|
|
if(hook == NULL) {
|
|
static int nspurious[NR_IRQ_VECTORS], report_interval = 100;
|
|
nspurious[irq]++;
|
|
if(nspurious[irq] == 1 || !(nspurious[irq] % report_interval)) {
|
|
printf("irq_handle: spurious irq %d (count: %d); keeping masked\n",
|
|
irq, nspurious[irq]);
|
|
if(report_interval < INT_MAX/2)
|
|
report_interval *= 2;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* 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[irq] |= hook->id;
|
|
|
|
/* Call the hooked function. */
|
|
if( (*hook->handler)(hook) )
|
|
irq_actids[hook->irq] &= ~hook->id;
|
|
|
|
/* Next hooked function. */
|
|
hook = hook->next;
|
|
}
|
|
|
|
/* reenable the IRQ only if there is no active handler */
|
|
if (irq_actids[irq] == 0)
|
|
hw_intr_unmask(irq);
|
|
|
|
hw_intr_ack(irq);
|
|
}
|
|
|
|
/* Enable/Disable a interrupt line. */
|
|
void enable_irq(const irq_hook_t *hook)
|
|
{
|
|
if((irq_actids[hook->irq] &= ~hook->id) == 0) {
|
|
hw_intr_unmask(hook->irq);
|
|
}
|
|
}
|
|
|
|
/* Return true if the interrupt was enabled before call. */
|
|
int disable_irq(const irq_hook_t *hook)
|
|
{
|
|
if(irq_actids[hook->irq] & hook->id) /* already disabled */
|
|
return 0;
|
|
irq_actids[hook->irq] |= hook->id;
|
|
hw_intr_mask(hook->irq);
|
|
return TRUE;
|
|
}
|
|
|