Mercurial > flash_v2
diff packages/kernel/current/src/intr/intr.cxx @ 0:3111d98ba7b3 ecos-v1_1-release
Initial commit of eCos version 1.1
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 11:16:07 +0000 |
| parents | |
| children | 443894e2e912 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/kernel/current/src/intr/intr.cxx @@ -0,0 +1,523 @@ +//========================================================================== +// +// intr/intr.cxx +// +// Interrupt class implementations +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Cygnus eCos Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://sourceware.cygnus.com/ecos +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Cygnus Operating System, released +// September 30, 1998. +// +// The Initial Developer of the Original Code is Cygnus. Portions created +// by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 1997-10-15 +// Purpose: Interrupt class implementation +// Description: This file contains the definitions of the interrupt +// class. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/kernel.h> + +#include <cyg/kernel/ktypes.h> // base kernel types +#include <cyg/infra/cyg_trac.h> // tracing macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/kernel/instrmnt.h> // instrumentation + +#include <cyg/kernel/intr.hxx> // our header + +#include <cyg/kernel/sched.hxx> // scheduler + +#include <cyg/kernel/sched.inl> + +// ------------------------------------------------------------------------- + +Cyg_Interrupt::Cyg_Interrupt( + cyg_vector vec, // Vector to attach to + cyg_priority pri, // Queue priority + CYG_ADDRWORD d, // Data pointer + cyg_ISR *ir, // Interrupt Service Routine + cyg_DSR *dr // Deferred Service Routine + ) +{ + vector = vec; + priority = pri; + isr = ir; + dsr = dr; + data = d; + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST + + dsr_count = 0; + next_dsr = NULL; + +#endif + +#ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN + + next = NULL; + +#endif + +}; + +// ------------------------------------------------------------------------- + +Cyg_Interrupt::~Cyg_Interrupt() +{ + detach(); +}; + +// ------------------------------------------------------------------------- +// DSR handling statics: + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE + +Cyg_Interrupt *Cyg_Interrupt::dsr_table[CYGNUM_KERNEL_INTERRUPTS_DSRS_TABLE_SIZE]; + +cyg_ucount32 Cyg_Interrupt::dsr_table_head = 0; + +cyg_ucount32 Cyg_Interrupt::dsr_table_tail = 0; + +#endif + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST + +Cyg_Interrupt *Cyg_Interrupt::dsr_list = NULL; + +#endif + +// ------------------------------------------------------------------------- +// Call any pending DSRs + +void Cyg_Interrupt::call_pending_DSRs() +{ +// CYG_REPORT_FUNCTION(); + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE + + while( dsr_table_head != dsr_table_tail ) + { + Cyg_Interrupt *intr = dsr_table[dsr_table_head]; + + dsr_table_head++; + if( dsr_table_head >= CYGNUM_KERNEL_INTERRUPTS_DSRS_TABLE_SIZE ) + dsr_table_head = 0; + + CYG_INSTRUMENT_INTR(CALL_DSR, intr->vector, 0); + + intr->dsr( intr->vector, 1, (CYG_ADDRWORD)intr->data ); + } + +#endif + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST + + while( dsr_list != NULL ) + { + Cyg_Interrupt *intr; + cyg_uint32 old_intr; + cyg_count32 count; + + HAL_DISABLE_INTERRUPTS(old_intr); + + intr = dsr_list; + dsr_list = intr->next_dsr; + count = intr->dsr_count; + intr->dsr_count = 0; + + HAL_RESTORE_INTERRUPTS(old_intr); + + intr->dsr( intr->vector, count, (CYG_ADDRWORD)intr->data ); + + } + +#endif + +}; + +// ------------------------------------------------------------------------- + +void Cyg_Interrupt::post_dsr() +{ +// CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_INTR(POST_DSR, vector, 0); + + cyg_uint32 old_intr; + + // We need to disable interrupts during this part to + // guard against nested interrupts. + + HAL_DISABLE_INTERRUPTS(old_intr); + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE + + dsr_table[dsr_table_tail++] = this; + if( dsr_table_tail >= CYGNUM_KERNEL_INTERRUPTS_DSRS_TABLE_SIZE ) + dsr_table_tail = 0; + +#endif + +#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST + + // Only add the interrupt to the dsr list if this is + // the first DSR call. + // At present DSRs are pushed onto the list and will be + // called in reverse order. We do not define the order + // in which DSRs are called, so this is acceptable. + + if( dsr_count++ == 0 ) + { + next_dsr = dsr_list; + dsr_list = this; + } + +#endif + + HAL_RESTORE_INTERRUPTS(old_intr); +}; + +// ------------------------------------------------------------------------- + +externC void interrupt_end( + cyg_uint32 isr_ret, + Cyg_Interrupt *intr, + HAL_SavedRegisters *regs + ) +{ +// CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_INTR(END, intr->vector, isr_ret); + +#ifndef CYGIMP_KERNEL_INTERRUPTS_CHAIN + + // Only do this if we are in a non-chained configuration. + // If we are chained, then chain_isr below will do the DSR + // posting. + + if( isr_ret & 2 ) intr->post_dsr(); + +#endif + +#ifdef CYGDBG_KERNEL_DEBUG_GDB_THREAD_SUPPORT + + // If we have GDB support enabled, and there is the possibility + // that this thread will be context switched as a result of this + // interrupt, then save the pointer to the saved thread context in + // the thread object so that GDB can get a meaningful context to + // look at. + + Cyg_Scheduler::get_current_thread()->set_saved_context(regs); + +#endif + + // Now unlock the scheduler, which may also call DSRs + // and cause a thread switch to happen. + + Cyg_Scheduler::unlock(); + +#ifdef CYGDBG_KERNEL_DEBUG_GDB_THREAD_SUPPORT + + Cyg_Scheduler::get_current_thread()->set_saved_context(0); + +#endif + + + CYG_INSTRUMENT_INTR(RESTORE, intr->vector, 0); +} + +// ------------------------------------------------------------------------- +// Interrupt chaining statics. + +#ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN + +Cyg_Interrupt *Cyg_Interrupt::chain_list[CYG_ISR_COUNT]; + +#endif + +// ------------------------------------------------------------------------- +// Chaining ISR inserted in HAL vector + +#ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN + +cyg_uint32 Cyg_Interrupt::chain_isr(cyg_vector vector, CYG_ADDRWORD data) +{ + Cyg_Interrupt *p = *(Cyg_Interrupt **)data; + + CYG_INSTRUMENT_INTR(CHAIN_ISR, vector, 0); + + while( p != NULL ) + { + if( p->vector == vector ) + { + register cyg_uint32 isr_ret = p->isr(vector, p->data); + + if( isr_ret & 2 ) p->post_dsr(); + + if( isr_ret & 1 ) break; + } + + p = p->next; + } + + return 0; +} + +#endif + +// ------------------------------------------------------------------------- +// Attach an ISR to an interrupt vector. + +void Cyg_Interrupt::attach() +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_ISR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_ISR_MAX, "Invalid vector"); + + CYG_INSTRUMENT_INTR(ATTACH, vector, 0); + + HAL_INTERRUPT_SET_LEVEL( vector, priority ); + +#ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN + + CYG_ASSERT( next == NULL , "Cyg_Interrupt already on a list"); + + cyg_uint32 index; + + HAL_TRANSLATE_VECTOR( vector, index ); + + if( chain_list[index] == NULL ) + { + // First Interrupt on this chain, just assign it and register + // the chain_isr with the HAL. + + chain_list[index] = this; + + HAL_INTERRUPT_ATTACH( vector, chain_isr, &chain_list[index], NULL ); + } + else + { + // There are already interrupts chained, add this one into the + // chain in priority order. + + Cyg_Interrupt **p = &chain_list[index]; + + while( *p != NULL ) + { + Cyg_Interrupt *n = *p; + + if( n->priority < priority ) break; + + p = &n->next; + } + next = *p; + *p = this; + } + +#else + + HAL_INTERRUPT_ATTACH( vector, isr, data, this ); + +#endif +} + +// ------------------------------------------------------------------------- +// Detach the ISR from the vector + +void Cyg_Interrupt::detach() +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_ISR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_ISR_MAX, "Invalid vector"); + + CYG_INSTRUMENT_INTR(DETACH, vector, 0); + +#ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN + + // Remove the interrupt object from the vector chain. + + Cyg_Interrupt **p = &chain_list[vector]; + + while( *p != NULL ) + { + Cyg_Interrupt *n = *p; + + if( n == this ) + { + *p = next; + break; + } + + p = &n->next; + } + + // If this was the last one, detach the vector. + + if( chain_list[vector] == NULL ) + HAL_INTERRUPT_DETACH( vector, chain_isr ); + +#else + + HAL_INTERRUPT_DETACH( vector, isr ); + +#endif + +} + +// ------------------------------------------------------------------------- +// Get the current service routine + +void Cyg_Interrupt::get_vsr(cyg_vector vector, cyg_VSR **vsr) +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_VSR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_VSR_MAX, "Invalid vector"); + + HAL_VSR_GET( vector, vsr ); +} + +// ------------------------------------------------------------------------- +// Install a vector service routine + +void Cyg_Interrupt::set_vsr(cyg_vector vector, cyg_VSR *vsr, cyg_VSR **old) +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_INTR(SET_VSR, vector, vsr); + + CYG_ASSERT( vector >= CYG_VSR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_VSR_MAX, "Invalid vector"); + + CYG_INTERRUPT_STATE old_ints; + + HAL_DISABLE_INTERRUPTS(old_ints); + + HAL_VSR_SET( vector, vsr, old ); + + HAL_RESTORE_INTERRUPTS(old_ints); +} + +// ------------------------------------------------------------------------- +// Disable interrupts at the CPU + + +void Cyg_Interrupt::disable_interrupts() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_INTR(DISABLE, 0, 0); + + CYG_INTERRUPT_STATE dummy; + + HAL_DISABLE_INTERRUPTS(dummy); + + // Keep a disable level counter?? +} + + +// ------------------------------------------------------------------------- +// Re-enable CPU interrupts + +void Cyg_Interrupt::enable_interrupts() +{ + CYG_REPORT_FUNCTION(); + + CYG_INSTRUMENT_INTR(ENABLE, 0, 0); + + HAL_ENABLE_INTERRUPTS(); +} + +// ------------------------------------------------------------------------- +// Mask a specific interrupt in a PIC + +void Cyg_Interrupt::mask_interrupt(cyg_vector vector) +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_ISR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_ISR_MAX, "Invalid vector"); + + CYG_INSTRUMENT_INTR(MASK, vector, 0); + + HAL_INTERRUPT_MASK( vector ) +} + +// ------------------------------------------------------------------------- +// Clear PIC mask + +void Cyg_Interrupt::unmask_interrupt(cyg_vector vector) +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_ISR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_ISR_MAX, "Invalid vector"); + + CYG_INSTRUMENT_INTR(UNMASK, vector, 0); + + HAL_INTERRUPT_UNMASK( vector ); +} + + +// ------------------------------------------------------------------------- +// Acknowledge interrupt at PIC + +void Cyg_Interrupt::acknowledge_interrupt(cyg_vector vector) +{ +// CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_ISR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_ISR_MAX, "Invalid vector"); + + CYG_INSTRUMENT_INTR(ACK, vector, 0); + + HAL_INTERRUPT_ACKNOWLEDGE( vector ); +} + +// ------------------------------------------------------------------------- +// Change interrupt detection at PIC + +void Cyg_Interrupt::configure_interrupt( + cyg_vector vector, // vector to control + cyg_bool level, // level or edge triggered + cyg_bool up // hi/lo level, rising/falling edge + ) +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( vector >= CYG_ISR_MIN, "Invalid vector"); + CYG_ASSERT( vector <= CYG_ISR_MAX, "Invalid vector"); + + CYG_INSTRUMENT_INTR(CONFIGURE, vector, (level<<1)|up); + + HAL_INTERRUPT_CONFIGURE( vector, level, up ); +} + +// ------------------------------------------------------------------------- +// EOF intr/intr.cxx
