#include <etherboot.h>
#include <pic8259.h>
#ifdef DEBUG_IRQ
#define DBG(...) printf ( __VA_ARGS__ )
#else
#define DBG(...)
#endif
int install_irq_handler ( irq_t irq, segoff_t *handler,
uint8_t *previously_enabled,
segoff_t *previous_handler ) {
segoff_t *irq_vector = IRQ_VECTOR ( irq );
*previously_enabled = irq_enabled ( irq );
if ( irq > IRQ_MAX ) {
DBG ( "Invalid IRQ number %d\n" );
return 0;
}
previous_handler->segment = irq_vector->segment;
previous_handler->offset = irq_vector->offset;
if ( *previously_enabled ) disable_irq ( irq );
DBG ( "Installing handler at %hx:%hx for IRQ %d, leaving %s\n",
handler->segment, handler->offset, irq,
( *previously_enabled ? "enabled" : "disabled" ) );
DBG ( "...(previous handler at %hx:%hx)\n",
previous_handler->segment, previous_handler->offset );
irq_vector->segment = handler->segment;
irq_vector->offset = handler->offset;
if ( *previously_enabled ) enable_irq ( irq );
return 1;
}
int remove_irq_handler ( irq_t irq, segoff_t *handler,
uint8_t *previously_enabled,
segoff_t *previous_handler ) {
segoff_t *irq_vector = IRQ_VECTOR ( irq );
if ( irq > IRQ_MAX ) {
DBG ( "Invalid IRQ number %d\n" );
return 0;
}
if ( ( irq_vector->segment != handler->segment ) ||
( irq_vector->offset != handler->offset ) ) {
DBG ( "Cannot remove handler for IRQ %d\n" );
return 0;
}
DBG ( "Removing handler for IRQ %d\n", irq );
disable_irq ( irq );
irq_vector->segment = previous_handler->segment;
irq_vector->offset = previous_handler->offset;
if ( *previously_enabled ) enable_irq ( irq );
return 1;
}
void send_specific_eoi ( irq_t irq ) {
DBG ( "Sending specific EOI for IRQ %d\n", irq );
outb ( ICR_EOI_SPECIFIC | ICR_VALUE(irq), ICR_REG(irq) );
if ( irq >= IRQ_PIC_CUTOFF ) {
outb ( ICR_EOI_SPECIFIC | ICR_VALUE(CHAINED_IRQ),
ICR_REG(CHAINED_IRQ) );
}
}
#ifdef DEBUG_IRQ
void dump_irq_status (void) {
int irq = 0;
for ( irq = 0; irq < 16; irq++ ) {
if ( irq_enabled ( irq ) ) {
printf ( "IRQ%d enabled, ISR at %hx:%hx\n", irq,
IRQ_VECTOR(irq)->segment,
IRQ_VECTOR(irq)->offset );
}
}
}
#endif
void (*undi_irq_handler)P((void)) = _undi_irq_handler;
uint16_t volatile *undi_irq_trigger_count = &_undi_irq_trigger_count;
segoff_t *undi_irq_chain_to = &_undi_irq_chain_to;
uint8_t *undi_irq_chain = &_undi_irq_chain;
irq_t undi_irq_installed_on = IRQ_NONE;
segoff_t *pxenv_undi_entrypointsp = &_pxenv_undi_entrypointsp;
uint8_t *pxenv_undi_irq = &_pxenv_undi_irq;
static uint16_t undi_irq_previous_trigger_count = 0;
int install_undi_irq_handler ( irq_t irq, segoff_t entrypointsp ) {
segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler);
if ( undi_irq_installed_on != IRQ_NONE ) {
DBG ( "Can install undi IRQ handler only once\n" );
return 0;
}
if ( SEGMENT(undi_irq_handler) > 0xffff ) {
DBG ( "Trivial IRQ handler not in base memory\n" );
return 0;
}
DBG ( "Installing undi IRQ handler on IRQ %d\n", irq );
*pxenv_undi_entrypointsp = entrypointsp;
*pxenv_undi_irq = irq;
if ( ! install_irq_handler ( irq, &undi_irq_handler_segoff,
undi_irq_chain,
undi_irq_chain_to ) )
return 0;
undi_irq_installed_on = irq;
DBG ( "Disabling undi IRQ %d\n", irq );
disable_irq ( irq );
*undi_irq_trigger_count = 0;
undi_irq_previous_trigger_count = 0;
DBG ( "UNDI IRQ handler installed successfully\n" );
return 1;
}
int remove_undi_irq_handler ( irq_t irq ) {
segoff_t undi_irq_handler_segoff = SEGOFF(undi_irq_handler);
if ( undi_irq_installed_on == IRQ_NONE ) return 1;
if ( irq != undi_irq_installed_on ) {
DBG ( "Cannot uninstall undi IRQ handler from IRQ %d; "
"is installed on IRQ %d\n", irq,
undi_irq_installed_on );
return 0;
}
if ( ! remove_irq_handler ( irq, &undi_irq_handler_segoff,
undi_irq_chain,
undi_irq_chain_to ) )
return 0;
if ( undi_irq_triggered ( undi_irq_installed_on ) ) {
DBG ( "Sending EOI for unwanted undi IRQ\n" );
send_specific_eoi ( undi_irq_installed_on );
}
undi_irq_installed_on = IRQ_NONE;
return 1;
}
int undi_irq_triggered ( irq_t irq ) {
uint16_t undi_irq_this_trigger_count = *undi_irq_trigger_count;
int triggered = ( undi_irq_this_trigger_count -
undi_irq_previous_trigger_count );
if ( irq == IRQ_NONE ) {};
undi_irq_previous_trigger_count = undi_irq_this_trigger_count;
return triggered ? 1 : 0;
}