All the interrupt information is generated in startup, store in syspage, then gets copied to one area.
syspage_init() gets the pointers to each syspage entry
qtimeptr = SYSPAGE_ENTRY(qtime); intrinfoptr = SYSPAGE_ENTRY(intrinfo); intrinfo_num = _syspage_ptr->intrinfo.entry_size / sizeof(*intrinfoptr); calloutptr = SYSPAGE_ENTRY(callout);
- initializes some interrupt variables based on syspage intrinfo: here we use intrinfo initialized here as an example:
- num_external_level = (32 + 192) + 32 = 256 ==>the sum of all iip->num_vectors
- interrupt_level is an array of struct interrupt_level, the size is: 256+ NUM_HOOK_RTNS.
- then let interrupt_level points at first external level: interrupt_level += NUM_HOOK_RTNS;
- ilp: each interrupt entry (GIC, SDMA, GPIO, etc) in syspage
- ilp->level_base = vector base of this entry;
- ilp->info = the pointer to the start of the interrupt entry
- for regular interrupt entry,
- ilp->mask_count = 1;
- ilp->cascade_level = -1
- for cascaded entry,
- ilp->cascade_level = vector base of this entry;
- ilp->mask_count = 0;
- write the interrupt entries to the processor/CPU vector table: see cpu_interrupt_init() for ARM
interrupt_attach(level, (handler), …). there is a global variable “count” in this function:
- find the interrupt_level: ilp = &interrupt_level[level];
- count the number of existing interrupt entries which have attached to this level/IRQ;
- allocate an interrupt entry “itp” by calling object_alloc().
- itp->thread = current thread;
- itp->level = level; // IRQ number
- itp->handler = handler
- add itp to the interrupt vector: id= vector_add(&interrupt_vector, itp, 0);
- itp->id = id;
- add itp to the table of existing interrupt entries for that level.
- if count == 0 (the first handler gets installed), call interrupt_unmask(level, NULL).
- remove the entry from the interrupt vector: itp = vector_rem();
- if there are no more interrupt entries attached to this interrupt_level[level],
- disable the interrupt: interrupt_mask();
- interrupt_level[level].mask_count = 1;
- itp->mask_count = 0;
- if there is still interrupt enties for this level, set need_maskcount_check = 1.
- remove interrpt handler from interrupt table.
interupt_unmask() —> unmask() callout
InterruptAttach(): Attach an interrupt handler to an IRQ. This call automatically enable (unmask) the interrupt.
- The first process to attach to an interrupt unmasks the interrupt. When the last process detaches from an interrupt, the system masks it.
- If the thread that attached the interrupt handler terminates without detaching the handler, the kernel does it automatically — different behaviorNTO_INTR_FLAGS_PROCESS is set!
Processor interrupts are enabled during the execution of the handler. Don’t attempt to talk to the interrupt controller chip. The operating system issues the end-of-interrupt command to the chip after processing all handlers at a given level.
- a few flags:
- for shared interrupt.
- adds the handler at the end of any existing handlers (default is to add in front of existing handlers).
- associates the interrupt handler with the process instead of the attaching thread.
- The interrupt handler is removed when the process exits, instead of when the attaching thread exits.
- _NTO_INTR_FLAGS_TRK_MSK flag and the id argument to InterruptMask() and InterruptUnmask() let the kernel track the number of times a particular interrupt handler or event has been masked. Then, when an application detaches from the interrupt, the kernel can perform the proper number of unmasks to ensure that the interrupt functions normally. This is important for shared interrupt levels.
Note: in my libraries, all the interrupts are attached using “_NTO_INTR_FLAGS_PROCESS” and “_NTO_INTR_FLAGS_TRK_MSK” flag.
These kernel calls detach the interrupt handler specified by the id argument. If, after detaching, no thread is attached to the interrupt, then the interrupt is masked off.The thread that detaches the interrupt handler must be in the same process as the thread that attached it.