You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
3.4 KiB
C

/* Copyright (C) 2021,2022 fef <owo@fef.moe>. All rights reserved. */
/*
* Most of the PIC management stuff is taken directly from the OSDev wiki:
* <https://wiki.osdev.org/PIC>
*/
#include <arch/cpufunc.h>
#include <arch/irq.h>
#include <arch/port.h>
#include <arch/trap.h>
#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
#define ICW1_INIT 0x10 /* Initialization - required! */
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
void arch_irq_init(void)
{
x86_set_intr_gate(X86_VECT_IRQ(0), _x86_isr_irq0);
x86_set_intr_gate(X86_VECT_IRQ(1), _x86_isr_irq1);
x86_set_intr_gate(X86_VECT_IRQ(3), _x86_isr_irq3);
x86_set_intr_gate(X86_VECT_IRQ(4), _x86_isr_irq4);
x86_set_intr_gate(X86_VECT_IRQ(5), _x86_isr_irq5);
x86_set_intr_gate(X86_VECT_IRQ(6), _x86_isr_irq6);
x86_set_intr_gate(X86_VECT_IRQ(7), _x86_isr_irq7);
x86_set_intr_gate(X86_VECT_IRQ(8), _x86_isr_irq8);
x86_set_intr_gate(X86_VECT_IRQ(9), _x86_isr_irq9);
x86_set_intr_gate(X86_VECT_IRQ(10), _x86_isr_irq10);
x86_set_intr_gate(X86_VECT_IRQ(11), _x86_isr_irq11);
x86_set_intr_gate(X86_VECT_IRQ(12), _x86_isr_irq12);
x86_set_intr_gate(X86_VECT_IRQ(13), _x86_isr_irq13);
x86_set_intr_gate(X86_VECT_IRQ(14), _x86_isr_irq14);
x86_set_intr_gate(X86_VECT_IRQ(15), _x86_isr_irq15);
/*
* disable interrupts to prevent IRQs from being fired
* immediately after the initialization sequence is complete
* (we need time to mask all IRQs again first)
*/
disable_intr();
/* begin initialization sequence in cascade mode */
x86_outb_wait(X86_PORT_PIC1_CMD, ICW1_INIT | ICW1_ICW4);
x86_outb_wait(X86_PORT_PIC2_CMD, ICW1_INIT | ICW1_ICW4);
/* tell PIC1 its offset into the vector table */
x86_outb_wait(X86_PORT_PIC1_DATA, X86_VECT_IRQ_BASE);
/* tell PIC2 its offset into the vector table */
x86_outb_wait(X86_PORT_PIC2_DATA, X86_VECT_IRQ_BASE + 8);
/* tell PIC1 that PIC2 is cascading into it on IRQ 2 */
x86_outb_wait(X86_PORT_PIC1_DATA, 1 << 2);
/* tell PIC2 that it is the second controller in the cascade chain */
x86_outb_wait(X86_PORT_PIC2_DATA, 2);
/* don't ask me what the fuck this does */
x86_outb_wait(X86_PORT_PIC1_DATA, ICW4_8086);
x86_outb_wait(X86_PORT_PIC2_DATA, ICW4_8086);
/*
* The initialization sequence is finished now, meaning all IRQ masks
* have been cleared (they are all enabled). Disable them again.
*/
x86_outb_wait(X86_PORT_PIC1_DATA, 0xff);
x86_outb_wait(X86_PORT_PIC2_DATA, 0xff);
/* it's safe to turn interrupts back on now */
enable_intr();
}
void arch_irq_enable(unsigned int number)
{
u16 port;
if (number >= 8) {
port = X86_PORT_PIC2_DATA;
number -= 8;
} else {
port = X86_PORT_PIC1_DATA;
}
u8 mask = x86_inb(port);
mask &= ~(1 << number);
x86_outb(port, mask);
}
void arch_irq_disable(unsigned int number)
{
u16 port;
if (number >= 8) {
port = X86_PORT_PIC2_DATA;
number -= 8;
} else {
port = X86_PORT_PIC1_DATA;
}
u8 mask = x86_inb(port);
mask |= (1 << number);
x86_outb(port, mask);
}
void x86_irq_ack(unsigned int number)
{
if (number >= 8)
x86_outb(X86_PORT_PIC2_CMD, 0x20);
x86_outb(X86_PORT_PIC1_CMD, 0x20);
}