diff options
Diffstat (limited to '410kern/x86/pic.c')
| -rw-r--r-- | 410kern/x86/pic.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/410kern/x86/pic.c b/410kern/x86/pic.c new file mode 100644 index 0000000..290e4db --- /dev/null +++ b/410kern/x86/pic.c @@ -0,0 +1,123 @@ +/** @file 410kern/x86/pic.c + * @brief Provides initialization functions for the Programmable Interrupt + * Controllers. + */ + +#define X86_PIC_DEFINITIONS +#include <x86/pic.h> +#include <x86/asm.h> + + /** @brief Bring up the PICs on this system. + * + * @param master_base is the offset into the IDT that the first PIC + * uses for its IRQ0. (So master_base through master_base + 7 + * will be used.) + * + * @param slave_base is the offset into the IDT that the slave PIC + * uses for its IRQ0. + * + * @post This function will enable IRQs on the PICs; EFLAGS:IF still + * can be used to disable interrupts. It is therefore suggested + * that this be called before IF is enabled the first time. + * + * @note This function assumes that there are only two PICs and that + * they are layed out as is conventional in IO space and that + * they are ganged together through master's IRQ2. The last + * system that violated these assumptions went offline roughly + * forever ago. If you don't like these, use the APICs which + * are better for you anyway. + */ +void +pic_init( unsigned char master_base, unsigned char slave_base ) +{ + /* Bring the master IDT up; yes I know that the IO looks odd. + * It's supposed to be, apparently. + */ + outb( MASTER_ICW, PICM_ICW1 ); + outb( MASTER_OCW, master_base ); + outb( MASTER_OCW, PICM_ICW3 ); + outb( MASTER_OCW, PICM_ICW4 ); + + /* Same dance with the slave as with the master */ + outb( SLAVE_ICW, PICS_ICW1 ); + outb( SLAVE_OCW, slave_base ); + outb( SLAVE_OCW, PICS_ICW3 ); + outb( SLAVE_OCW, PICS_ICW4 ); + + /* Tell the master and slave that any IRQs they had outstanding + * have been acknowledged. + */ + outb( MASTER_ICW, NON_SPEC_EOI ); + outb( SLAVE_ICW, NON_SPEC_EOI ); + + /* Enable all IRQs on master and slave */ + outb ( SLAVE_OCW, 0 ); + outb ( MASTER_OCW, 0 ); +} + + /** @brief Acknowledge the master or slave correctly, based on the + * IRQ number given. + * + * @param irq The IRQ, from the master PIC's perspective, that is to + * be acknowledged. This means that 0-7 are master IRQs and + * 8-15 are slave. + */ +void +pic_acknowledge(unsigned char irq) +{ + /* Note that SEND_EOI_IRn is just n, so we don't need any fancy map */ + if ( irq <= 7 ) { + outb(MASTER_ICW, SPECIFIC_EOI | irq ); + } else if ( irq <= 15 ) { + outb(SLAVE_ICW, SPECIFIC_EOI | (irq & 0x07) ); + outb(MASTER_ICW, SPECIFIC_EOI | PICS_ICW3 ); + } else { + return; + } +} + + /** @brief Acknowledge the master PIC for an arbitrary interrupt. */ +void +pic_acknowledge_any_master(void) +{ + outb(MASTER_ICW, NON_SPEC_EOI ); +} + + /** @brief Acknowledge the slave PIC for an arbitrary interrupt. + * @note Also acknowledges the master's input from the slave. + */ +void +pic_acknowledge_any_slave(void) +{ + outb(SLAVE_ICW, NON_SPEC_EOI ); + outb(MASTER_ICW, SPECIFIC_EOI | PICS_ICW3 ); +} + + /** @brief Test if a given IRQ is active. + * + * @pre Arguably interrupts should be off. + * + * @param irq The IRQ, from the master PIC's perspective, that is to + * be acknowledged. This means that 0-7 are master IRQs and + * 8-15 are slave. + * + * @return True if active, false if not (including bogus IRQ values) + */ +int +pic_irq_active(unsigned char irq) +{ + unsigned char port; + unsigned char mask; + + if ( irq > 15 ) { + return 0; + } else if ( irq <= 7 ) { + port = MASTER_ICW; + mask = 1 << irq; + } else { + port = SLAVE_ICW; + mask = 1 << (irq - 8); + } + outb(port, READ_IS_ONRD | OCW_TEMPLATE | READ_NEXT_RD); + return ((inb(port) & mask) != 0); +} |
