/* * irq.c * * Copyright (C) 2013 Aleksandar Andrejevic * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include static list_entry_t irq_handlers[MAX_IRQ_COUNT]; static dword_t irq_alloc_bitmap = 0xFFFFF3FF; static byte_t primary_pic_mask = 0xFF, secondary_pic_mask = 0xFF; static void irq_handler(registers_t *regs, byte_t int_num) { irq_handler_t *handler; byte_t irq_num = int_num - IRQ_INT_BASE; if (irq_num == 7) { cpu_write_port_byte(PRIMARY_PIC_CMD, 0x0B); byte_t isr = cpu_read_port_byte(PRIMARY_PIC_CMD); if (!(isr & 0x80)) return; } if (irq_num == 15) { cpu_write_port_byte(SECONDARY_PIC_CMD, 0x0B); byte_t isr = cpu_read_port_byte(SECONDARY_PIC_CMD); if (!(isr & 0x80)) { cpu_write_port_byte(PRIMARY_PIC_CMD, 0x20); return; } } list_entry_t *ptr; for (ptr = irq_handlers[irq_num].next; ptr != &irq_handlers[irq_num]; ptr = ptr->next) { handler = CONTAINER_OF(ptr, irq_handler_t, list); cpu_enable_interrupts(); handler->procedure(regs, irq_num); cpu_disable_interrupts(); } if (irq_num >= 8) cpu_write_port_byte(SECONDARY_PIC_CMD, 0x20); cpu_write_port_byte(PRIMARY_PIC_CMD, 0x20); } dword_t register_irq_handler(byte_t irq_num, irq_handler_proc_t handler_proc, bool_t exclusive) { dword_t ret = ERR_SUCCESS; critical_t critical; if (irq_num >= MAX_IRQ_COUNT) return ERR_INVALID; irq_handler_t *handler = (irq_handler_t*)malloc(sizeof(irq_handler_t)); if (handler == NULL) return ERR_NOMEMORY; handler->procedure = handler_proc; handler->exclusive = exclusive; enter_critical(&critical); if (irq_handlers[irq_num].next == &irq_handlers[irq_num]) { if (irq_num >= 0 && irq_num < 8) { primary_pic_mask &= ~(1 << irq_num); cpu_write_port_byte(PRIMARY_PIC_DATA, primary_pic_mask); } else { secondary_pic_mask &= ~(1 << (irq_num - 8)); cpu_write_port_byte(SECONDARY_PIC_DATA, secondary_pic_mask); } } else if (exclusive || CONTAINER_OF(irq_handlers[irq_num].next, irq_handler_t, list)->exclusive) { ret = ERR_EXISTS; } if (ret == ERR_SUCCESS) list_append(&irq_handlers[irq_num], &handler->list); leave_critical(&critical); return ret; } dword_t unregister_irq_handler(byte_t irq_num, irq_handler_proc_t handler_proc) { bool_t found = FALSE; critical_t critical; if (irq_num >= MAX_IRQ_COUNT) return ERR_INVALID; enter_critical(&critical); list_entry_t *ptr; for (ptr = irq_handlers[irq_num].next; ptr != &irq_handlers[irq_num]; ptr = ptr->next) { irq_handler_t *handler = CONTAINER_OF(ptr, irq_handler_t, list); if (handler->procedure == handler_proc) { list_remove(&handler->list); free(handler); found = TRUE; break; } } leave_critical(&critical); return found ? ERR_SUCCESS : ERR_NOTFOUND; } byte_t alloc_irq() { byte_t i; for (i = 0; i < MAX_IRQ_COUNT; i++) if (!test_bit(&irq_alloc_bitmap, i)) { set_bit(&irq_alloc_bitmap, i); return i; } return 0xFF; } void free_irq(byte_t number) { clear_bit(&irq_alloc_bitmap, number); } void irq_init() { byte_t i; primary_pic_mask &= ~(1 << PRIMARY_PIC_CASCADE_IRQ); cpu_write_port_byte(PRIMARY_PIC_CMD, 0x11); cpu_write_port_byte(SECONDARY_PIC_CMD, 0x11); cpu_write_port_byte(PRIMARY_PIC_DATA, PRIMARY_IRQ_INT); cpu_write_port_byte(SECONDARY_PIC_DATA, SECONDARY_IRQ_INT); cpu_write_port_byte(PRIMARY_PIC_DATA, 1 << PRIMARY_PIC_CASCADE_IRQ); cpu_write_port_byte(SECONDARY_PIC_DATA, 1 << SECONDARY_PIC_CASCADE_IRQ); cpu_write_port_byte(PRIMARY_PIC_DATA, PIC_8086_MODE); cpu_write_port_byte(SECONDARY_PIC_DATA, PIC_8086_MODE); cpu_write_port_byte(PRIMARY_PIC_DATA, primary_pic_mask); cpu_write_port_byte(SECONDARY_PIC_DATA, secondary_pic_mask); for (i = 0; i < MAX_IRQ_COUNT; i++) { list_init(&irq_handlers[i]); set_int_handler(IRQ_INT_BASE + i, irq_handler, FALSE, FALSE); } }