1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
//! Configure legacy PIC 8259
use bitfield_struct::bitfield;
use crate::arch::io::Port;
// PIC 1 config
const PIC1_OFFSET: u8 = 0x20;
const PIC1_CMD: Port<u8> = Port::new(0x20);
const PIC1_DATA: Port<u8> = Port::new(0x21);
// PIC 2 config
const PIC2_OFFSET: u8 = 0x28;
const PIC2_CMD: Port<u8> = Port::new(0xa0);
const PIC2_DATA: Port<u8> = Port::new(0xa1);
/// Command sent to begin PIC initialization.
const PIC_CMD_INIT: u8 = 0x11;
/// Command sent to acknowledge an interrupt.
const PIC_CMD_EOI: u8 = 0x20;
/// 8086/8088 or 8085 mode.
const PIC_MODE_8086: u8 = 0x01;
/// Single or multiple (cascade mode) 8259A
const PIC_MODE_AUTO_EOI: u8 = 0x02;
/// Interrupt ports of the PIC
#[bitfield(u16)]
pub struct PicPort {
pub timer: bool,
pub keyboard: bool,
chain: bool, // chain
pub serial1: bool,
pub serial2: bool,
pub parallel23: bool,
pub floppy: bool,
pub parallel1: bool,
pub rtc: bool,
pub acpi: bool,
pub p10: bool,
pub p11: bool,
pub mouse: bool,
pub co_processor: bool,
pub primart_ata: bool,
pub secondary_ata: bool,
}
/// Initialize the chained 8259 PICs as shown below.
///
/// Where `enable` specifies which interrupts are enabled.
///
/// ```text
/// ____________ ____________
/// Real Time Clock --> | 40 | Timer -------------> | 32 |
/// ACPI -------------> | | Keyboard-----------> | |
/// Available --------> | Secondary |----------------------> | Primary |
/// Available --------> | Interrupt | Serial Port 2 -----> | Interrupt |->
/// Mouse ------------> | Controller | Serial Port 1 -----> | Controller |
/// Co-Processor -----> | | Parallel Port 2/3 -> | |
/// Primary ATA ------> | | Floppy disk -------> | |
/// Secondary ATA ----> |_47_________| Parallel Port 1----> |_39_________|
/// ```
pub fn init(enable: PicPort) {
unsafe {
// ICW1: 8086 mode with ICW4
PIC1_CMD.write(PIC_CMD_INIT);
PIC2_CMD.write(PIC_CMD_INIT);
// ICW2 Master: IRQ # Offset (32)
PIC1_DATA.write(PIC1_OFFSET);
// ICW2 Slave: IRQ # Offset (40)
PIC2_DATA.write(PIC2_OFFSET);
// ICW3 Master: slaves on IRQs
PIC1_DATA.write(0x04);
// ICW3 Slave: connect to IRQ2 of the masters
PIC2_DATA.write(0x02);
//ICW4: 8086 mode
PIC1_DATA.write(PIC_MODE_8086 | PIC_MODE_AUTO_EOI);
PIC2_DATA.write(PIC_MODE_8086 | PIC_MODE_AUTO_EOI);
// Set interrupt mask
let [l, h] = (!enable.with_chain(true).0).to_le_bytes();
PIC1_DATA.write(l);
PIC2_DATA.write(h);
}
}
/// Set the End Of Interrupt bit to allow new interrupts.
pub fn eoi(vector: u8) {
const PIC1_END: u8 = PIC1_OFFSET + 7;
const PIC2_END: u8 = PIC2_OFFSET + 7;
match vector {
PIC1_OFFSET..=PIC1_END => unsafe { PIC1_CMD.write(PIC_CMD_EOI) },
PIC2_OFFSET..=PIC2_END => unsafe { PIC2_CMD.write(PIC_CMD_EOI) },
_ => {}
}
}
pub fn disable() {
unsafe {
// select Interrupt Mode Control Register (IMCR)
Port::<u8>::new(0x22).write(0x70);
// disable PIC Mode
Port::<u8>::new(0x23).write(0x01);
}
}