The ASSP/Variant part of the base port must implement an interrupt dispatcher to manage interrupts.
An interrupt source is a hardware device or software action that can force the CPU to suspend normal execution, enter interrupt handling state and jump to a section of code called an interrupt handler.
Typically, a number of interrupt sources are monitored by an interrupt controller. This is hardware that generates a single interrupt notification to the CPU, and provides information about which interrupts are pending, i.e. which interrupts require action to be taken.
An interrupt service routine, or ISR, is code that deals with a pending interrupt. The Symbian platform kernel responds to an interrupt notification by calling an ISR for each pending interrupt. The process of calling ISRs is called interrupt dispatch.
The ISR is a single bare function. It is not a class member.
Each ISR takes a single 32-bit parameter that is, typically, a pointer to an owning class, although it can be any value that is appropriate. The parameter is defined as a TAny * type, so a cast is almost always necessary.
ISRs are usually kept in an ISR table.
An interrupt source is identified by number, defined as a TInt type. Typically, the ASSP layer defines this number for each interrupt in a header file and exports it so that it can be included and used by device drivers.
Where the ASSP layer is split into a common layer and a variant (device specific) layer, then the variant layer may also define its own set of interrupt IDs.
This number is usually referred to as the interrupt ID.
Only one ISR can be associated with each possible interrupt source. Making this association is known as binding. ISRs can be bound and unbound during normal operation, but only one ISR can be bound to an interrupt source at any one time.
A device driver binds an ISR by calling Interrupt::Bind(), passing the interrupt source ID; similarly, the device driver can unbind the ISR by calling Interrupt::Unbind(), also passing the interrupt ID.
At its simplest, this is the process of deciding which interrupts are pending and calling the ISR for each.
The following pseudo code shows the general principle:
{ FOREVER { Get next pending interrupt; if None { return; } call ISR for the pending interrupt; } }
In practice the dispatcher may have to do some more work to communicate with the interrupt controller hardware.
A system may have multiple interrupt controllers to handle a large number of interrupt sources. These are usually prioritised by connecting the interrupt output of a lower-priority controller to an interrupt input of a higher-priority controller. This is called chaining.
An interrupt from a lower priority controller will appear as an interrupt on the highest-priority controller.
When the interrupt dispatcher of the higher-priority controller detects that it is the chained interrupt that is pending, the usual way of dealing with this is to run a secondary dispatcher to determine which interrupt on the chained controller is pending.
There may be further levels of chaining before the true source of the interrupt has been identified.
It is possible that a single input to an interrupt controller is shared by several interrupt sources.
It appears necessary to bind multiple ISRs to the same interrupt. However, this is not possible. There are two ways of dealing with this:
Maintain a list of all ISRs that are bound to this single interrupt source, and call all the ISRs in the list when the interrupt is dispatched. This is most conveniently implemented by binding a single ISR to the interrupt, which then calls all the real ISRs bound to this interrupt
Create pseudo interrupts. These are extra interrupt IDs that do not exist in the interrupt controller, but represent each of the interrupt sources connected to the single shared interrupt source. An ISR can then be bound to each pseudo interrupt. The interrupt dispatcher can then determine which of the sources are actually signalling and call the appropriate ISR via that pseudo interrupt ID. This is effectively an implementation of a chained interrupt, and assumes that the interrupt dispatcher can identify which of the sources is signalling.
When a common ASSP extension is used, a device may have additional peripherals external to the ASSP, and there needs to be a way of allowing extra interrupt binding and dispatch functions to be added later by the variant layer. This must be handled by the port as Symbian platform does not provide any additional API to support this.
Device drivers should be able to use the Interrupt class functions without having to know where the interrupt is actually implemented. This implies that all requests should go to the core implementation of functions like Interrupt::Bind(), Interrupt::Enable() etc.
To enable the core implementation of these functions to decide whether an interrupt ID refers to a core interrupt or device specific interrupt, a common technique is to "tag" the interrupt ID. A simple way is to use positive numbers to identify core interrupts and negative numbers to identify device specific interrupts. The ISRs for device specific interrupts are not stored in the core ISR table, instead the device specific layer provides its own ISR table.
The general pattern for creating the core-device specific split is that the core derives an implementation from class Asic, and the device specific part further derives from this core implementation. The usual technique is to add a set of virtual functions to the core class that can be derived by the device specific part. The core can provide default implementations for these functions that would just return KErrArgument to trap illegal ID numbers. This API would need functions equivalent to each of the functions defined by the Interrupt class.
As an example, the core layer for the template reference board defines a class TemplateAssp that is derived from Asic. TemplateAssp defines the pure virtual functions: InterruptBind(), InterruptUnbind(), InterruptEnable() etc, all with signatures that are the same for the comparable functions defined by Interrupt, and which are implemented by the Template class.
In the Kernel Architecture 2, it is a convention that unbound interrupts should be bound to a "spurious" interrupt handler, i.e. an interrupt handler that faults the system indicating the number of the interrupt. This aids debugging by identifying interrupts that are enabled without corresponding ISRs.
The interrupt architecture supports the concept of adjustable interrupt priorities. Symbian platform defines the Interrupt::SetPriority() function that can implement this. The function is passed the ID of the interrupt source to be adjusted together with a priority value. The meaning of the priority value is hardware and implementation dependent, and is defined by the port.
The Variant must provide a table where each entry defines which ISR is bound to which interrupt source. The table must have enough space for entries for each interrupt source that is known to the Variant.
When the Variant is split into an ASSP layer and a Variant layer, the ISR table is put in the ASSP layer and will not normally include ISRs for the Variant interrupt sources - these will be handled by separate chained dispatchers in the Variant layer.
Symbian platform provides the SInterruptHandler structure, defined in the header file ...\e32\include\kernel\arm\assp.h to encapsulate the entry for an ISR. The ISR table is, therefore, just an array of SInterruptHandler items. For example, if a system has 32 possible interrupt sources, then the ISR table would be defined as:
... const TInt KInterruptSourceCount = 32; SInterruptHandler IsrHandlers[KInterruptSourceCount]; ...
Interrupts are identified in the system by their interrupt ID number, which is used to index into the ISR table. You are free to allocate these numbers any way that is convenient for you.
On the template reference board, for example, the ISR table is defined as a static data member of the VariantASSPInterrupt class, which is derived from TemplateInterrupt. The class is defined in ...\template_assp\template_assp_priv.h.
class TemplateInterrupt : public Interrupt { ... // functions public: static SInterruptHandler Handlers[KNumTemplateInts]; ... };
where KNumTemplateInts is defined in the same header file.
Factors that decide the size of the ISR table
The number of entries to be reserved in the ISR table depends on the following factors:
Where the ASSP is targeted at only a single device, the number of possible interrupts is usually known, and the table can include an entry for each one.
If any pseudo sources exist, they should be included in the main table for efficiency, but note that this is not strictly necessary.
Other factors affecting the ISR table
IRQs and FIQs may need to be distinguished, although the exact requirement is hardware dependent. Although the table has one entry for each possible interrupt source, a possible scheme may be to group IRQs at the start of the table, and FIQs at the end of the table. If the hardware has separate interrupt controller hardware for IRQs and FIQs (or at least, different registers) then you will need to arrange the table so that you can determine from the interrupt ID whether the interrupt is an IRQ or FIQ.
For example:
Most of the interrupt dispatching code is implemented in the ASSP layer. This includes a list of ISRs, code for adding and removing ISRs, enabling and disabling interrupt sources, and dispatching ISRs. The kernel only provides a pre-amble and post-amble.
The kernel defines, but does not implement, a class called Interrupt that exports interrupt functionality to device drivers and other kernel code. The class provides the public API for using interrupts (but not for dispatching them). The port must provide an implementation for each function defined by the class.
The class is defined in the header files ...\e32\include\kernel\arm\assp.h, which is exported to ...\epoc32\include\kernel\arm.
See Symbian OS Internals Book, Chapter 6 - Interrupts and Exceptions
Copyright ©2010 Nokia Corporation and/or its subsidiary(-ies).
All rights
reserved. Unless otherwise stated, these materials are provided under the terms of the Eclipse Public License
v1.0.