I'm looking for any advice that might help me determine why IRQ4 is not triggering for each byte of serial data that is sent. I have not tested this on hardware yet because I don't have a serial cable at the moment. Thank you in advance for any help you can provide.
These are my QEMU flags:
Code: Select all
QEMU_FLAGS = -cdrom $(ISO_TARGET) -display cocoa,full-screen=on -monitor stdio -serial pty
Code: Select all
echo -n h > /dev/ttys002
Here is the relevant code:
irq.c
Code: Select all
void irq_remap(void) {
// ICW1
outb(PIC1_CMD, ICW1_ICW4 | ICW1_INIT);
outb(PIC2_CMD, ICW1_ICW4 | ICW1_INIT);
// ICW2
outb(PIC1_DATA, ICW2_PIC1_BASE);
outb(PIC2_DATA, ICW2_PIC2_BASE);
// ICW3
outb(PIC1_DATA, ICW3_PIC1_IRQ); // master PIC at IRQ4
outb(PIC2_DATA, ICW3_PIC2_IRQ); // slave PIC at IRQ2
// ICW4
outb(PIC1_DATA, ICW4_8086);
outb(PIC2_DATA, ICW4_8086);
// Unmask IRQ4 -- Todo: handle this in separate logic
outb(PIC1_DATA, 0xEF);
outb(PIC2_DATA, 0xFF);
}
static uint16_t pic_get_irq_reg(int ocw3) {
outb(PIC1_CMD, ocw3);
outb(PIC2_CMD, ocw3);
return (inb(PIC2_CMD) << 8) | inb(PIC1_CMD);
}
uint16_t pic_get_irr(void) {
return pic_get_irq_reg(PIC_OCW3_IRR);
}
uint16_t pic_get_isr(void) {
return pic_get_irq_reg(PIC_OCW3_ISR);
}
void pic_endof_int(uint8_t irq) {
if (irq >= 8)
outb(PIC2_CMD, PIC_EOI);
outb(PIC1_CMD, PIC_EOI);
}
void irq_handler() {
int irq = BIT_INDEX(pic_get_isr());
kprintf(".");
if (irq_handlers[irq] != NULL) {
irq_handlers[irq]();
}
outb(PIC1_DATA, 0xEF);
outb(PIC2_DATA, 0xFF);
pic_endof_int(irq);
}
void irq_set_gates() {
for (int i = IRQ_VEC_START; i < IRQ_VEC_END; ++i) {
idt_set_desc(i, &irq_stub, IDTENTRY_KERNEL_INT);
}
}
void irq_install() {
irq_remap();
irq_set_gates();
}
void register_irq_handler(int irq_num, void* handler) {
if (irq_num >= 0 && irq_num < MAX_IRQS) {
irq_handlers[irq_num] = handler;
}
}
Code: Select all
int serial_init(void) {
outb(SERIAL_PORT + IER_OFFSET, 0x01); // enable interrupts
outb(SERIAL_PORT + LCR_OFFSET, 0x80); // set DLAB
outb(SERIAL_PORT + DIVISOR_LSB_OFFSET, 0x01); // set LSB of divisor
outb(SERIAL_PORT + DIVISOR_MSB_OFFSET, 0x00); // set MSB of divisor
outb(SERIAL_PORT + LCR_OFFSET, 0x00); // clear DLAB
//outb(SERIAL_PORT + FCR_OFFSET, 0xC7); // enable FIFO, clear buffers, 14-byte threshold
outb(SERIAL_PORT + FCR_OFFSET, 0x00); // disable FIFO
outb(SERIAL_PORT + LCR_OFFSET, 0x03); // 8-bit, no parity, 1 stop bit
outb(SERIAL_PORT + MCR_OFFSET, 0x1E); // loopback mode, test the serial chip
outb(SERIAL_PORT, 0xAB);
if (inb(SERIAL_PORT) != 0xAB) {
return 1;
}
// serial is not faulty, set in normal operation mode
outb(SERIAL_PORT + MCR_OFFSET, 0x0F); // interrupt enabled, RTS and DSR set
register_irq_handler(SERIAL_PORT_IRQ, serial_irq_handler);
return 0;
}
static void serial_handle_receive_byte() {
uint8_t recb = inb(SERIAL_PORT);
kprintf("%c", recb);
}
static void serial_irq_handler() {
// Determine if we are reading/writing UART
uint8_t lsr = inb(SERIAL_PORT + LSR_OFFSET);
uint8_t iir = inb(SERIAL_PORT + IIR_OFFSET);
// Check if data is ready to be extracted from the UART
if (lsr & 1) {
serial_handle_receive_byte();
}
}
}