Interrupt::Bind() and Interrupt::Unbind()

Describes two types of configuration used in the implementation of bind and unbind functions.

The functions Interrupt::Bind() and Interrupt::Unbind() provide device drivers with a way of binding ISRs to interrupt sources, and unbinding ISRs from interrupt sources. The implementation of these functions follows a standard pattern that can be re-used in most cases.

The implementation can be different if there is a single Variant DLL, or if there is also an ASSP extension.

Variant DLL only configuration

The following example implementation of Interrupt::Bind() assumes that all ISRs in the ISR table have been bound, by default, to the spurious interrupts handler function as done in initialising the table. To bind an interrupt source, the spurious interrupts handler is replaced with the provided ISR. The implementation prevents an ISR from being bound to an interrupt source that is already bound, by assuming that an unbound interrupt source is bound to the spurious interrupts handler.

The implementation shows the basic idea but may need to be extended, especially where chained interrupts and/or multiple interrupt sources and pseudo interrupt sources are involved.

EXPORT_C TInt Interrupt::Bind(TInt aId, TIsr aIsr, TAny* aPtr)
    {
       TInt r = KErrNone;
    If(TUint(aId)>=TUint(KInterruptSourceCount))
        {
        r = KErrArgument; // Illegal interrupt number
        }
       else 
        {
           SInterruptHandler& h = IsrHandlers[aId];
        TInt irq = NKern::DisableAllInterrupts();
        if (h.iIsr != &SpuriousHandler)
            {
            r = KErrInUse; // Already bound to an ISR
            }
           else
            {
            h.iPtr = aPtr; // The ISR parameter
            h.iIsr = aIsr; // Pointer to the ISR
            }
        NKern::RestoreInterrupts(irq);
        }
    return r;
    }

Interrupt::Unbind() is the logical opposite of Bind(), replacing the ISR with the spurious handler function. The following code is the matching example implementation.

EXPORT_C TInt Interrupt::Unbind(TInt aId)
    {
       TInt r = KErrNone;
       if (TUint(aId) >= TUint(KInterruptSourceCount))
        {
        r = KErrArgument;    // Illegal interrupt number
        }
       else
        {
        SInterruptHandler& h = IsrHandlers[aId];
        TInt irq = NKern::DisableAllInterrupts();
        if (h.iIsr == &SpuriousHandler)
            {
            r = KErrGeneral; // Already unbound
            }
        else
            {
            h.iPtr =(TAny*)aId;
            h.iIsr = SpuriousHandler;  // Replace with spurious handler
            // NOTE: at this point it may be wise to
            // force the hardware interrupt source to disabled.
            }
        NKern::RestoreInterrupts(irq);
        }
    return r;
       }

Note that functions in the Interrupt class can be called from anywhere in kernel-side code, including ISRs, DFCs and IDFCs, and kernel threads. You need to ensure that if you are manipulating the ISR table, or any other structure that is shared, you need to disable interrupts to prevent corruption of the data. Interrupts are disabled and re-enabled using NKern::DisableAllInterrupts() and NKern::RestoreInterrupts().

ASSP Extension and Variant DLL configuration

When a common ASSP extension is used, the Variant DLL may have to implement extra interrupt binding and dispatch functions for the device-specific interrupts.

The following code is an example of an implementation of Interrupt::Bind() in the ASSP layer, where negative Interrupt IDs represent device specific interrupts.

EXPORT_C TInt Interrupt::Bind(TInt aId, TIsr aIsr, TAny* aPtr)
    {
    TInt r = KErrNone;
    if(aId < 0 )
        {
        return MyAsic->VariantBind( aId, aIsr, aPtr ); // Device specific ID, call variant
        }
    else if (aId >= KInterruptSourceCount)
            {
            r = KErrArgument; // Illegal interrupt number
            }
        else
            {
            SInterruptHandler& h = IsrHandlers[aId];
            TInt irq = NKern::DisableAllInterrupts();
            if (h.iIsr != SpuriousHandler)
                {
                r = KErrInUse; // Already bound to an ISR
                }
            else
                {
                h.iPtr = aPtr;
                h.iIsr = anIsr;
                }
            NKern::RestoreInterrupts(irq);
            }
    return r;
    }

The default device specific implementation of the VariantBind() function might be as follows:

TInt TMyAsic::VariantBind( TInt aId, TIsr aIsr, TAny* aPtr )
    // Default implementation when device specific layer does not override
    // and this is therefore an illegal aId.
    {
    return KErrArgument;
    }

The device specific implementation of VariantBind() would follow the same general pattern as for the Interrupt::Bind() function in the ASSP layer. The main difference is that the code refers to the device specific ISR table, VariantHandlers[], defined within the device specific layer, and the interrupt ID is converted to a positive number so that it can be used as an index into this table:

SInterruptHandler VariantHandlers[KNumVariantInts];
EXPORT_C TInt TMyVariant::VariantBind(TInt aId, TIsr aIsr, TAny* aPtr)
    {
    TInt r = KErrNone;
    aId = (-aId)-1;    // convert to positive number >=0
    If (aId >= KInterruptSourceCount || aId < 0)
        {
        r = KErrArgument; // Illegal interrupt number
        }
    else
        {
        SInterruptHandler& h = VariantHandlers[aId];
        TInt irq = NKern::DisableAllInterrupts();
        if (h.iIsr != VariantSpuriousHandler)
            {
            r = KErrInUse; // Already bound to an ISR
            }
        else
            {
            h.iPtr = aPtr;
            h.iIsr = anIsr;
            }
        NKern::RestoreInterrupts(irq);
        }
    return r;
    }

Now you need a way of dispatching the interrupts and since this is really a chained interrupt, the dispatch function can be packaged as an ISR and bound to the core interrupt source it chains from. See IRQ and FIQ Dispatchers in general and Dealing with chained interrupts in particular.