#pragma once // Hardware interrupt registration. // // Until this existed, trap() was a switch statement listing every interrupt // the kernel knew about, and adding a device meant editing the trap handler. // That is fine while the devices are the four the PC has always had at fixed // pins; it stops being fine the moment a device's IRQ is whatever the BIOS // decided to put in its PCI config space this boot. A driver cannot write // `case T_IRQ0 + IRQ_XHCI:` because there is no such constant -- it only // learns its line at probe time. // // So: a line has a chain of handlers, a driver adds itself to one at probe // time, and enabling the line is a consequence of registering rather than a // separate call somebody can forget. // // Chains matter more than they look. PCI interrupt pins are *shared*: four // INTx lines are routed to a handful of IOAPIC inputs, so two unrelated // devices routinely land on the same one. A handler must therefore check // whether its own device actually raised the interrupt and do nothing if it // did not. #define NIRQ 24 // IOAPIC redirection entries on a PC #define NCHAIN 4 // handlers that may share one line typedef void (*irq_handler_t)(void *arg); // Returns 0, or -1 if the line is out of range, reserved, or full. // Registering enables the line; the last unregister masks it again. // // `level_low` selects PCI's signalling (level-triggered, active low) rather // than ISA's (edge-triggered, active high). Getting this wrong does not fail // cleanly: an edge-configured level interrupt fires once and then wedges, // because the device keeps the line asserted and no further edge ever comes. int irq_register(int irq, irq_handler_t fn, void *arg, const char *name, int level_low); void irq_unregister(int irq, irq_handler_t fn, void *arg); int irq_handler_count(int irq); // Called from trap() with the IOAPIC pin number, not the vector. void irq_dispatch(int irq); void irq_init(void); int irq_info(char *buf, int cap); // /proc/interrupts