/* $Id: irq.cpp 205 2007-06-14 01:33:51Z stevenhl $ */ /* SCCSID = %W% %E% */ /**************************************************************************** * * * Copyright (c) IBM Corporation 1994 - 1997. * * * * The following IBM OS/2 source code is provided to you solely for the * * the purpose of assisting you in your development of OS/2 device drivers. * * You may use this code in accordance with the IBM License Agreement * * provided in the IBM Device Driver Source Kit for OS/2. * * * ****************************************************************************/ /**@internal %W% * IRQ object implementation. * @version %I% * @context * Unless otherwise noted, all interfaces are Ring-0, 16-bit, kernel stack. * @notes * Refer to .HPP file for interface rules. * @history */ #include #include #include // memset() #include "irq.hpp" // Object definition const int NumIrqLevels = 24; // We need a global array to keep track of the IRQ objects that have been created. // The interrupt-time ISR uses this array to find the object associated with // interrupt level "i". IRQ "i" is at position "i" in array. static IRQ* pIrqObject[ NumIrqLevels ] = { 0 }; // These are the ISR's that take the interrupt from the kernel. We've // defined handlers only for the IRQ's supported by this sound card. void far ISR03( void ) { pIrqObject[ 3]->CallHandlers(); } void far ISR04( void ) { pIrqObject[ 4]->CallHandlers(); } void far ISR05( void ) { pIrqObject[ 5]->CallHandlers(); } void far ISR07( void ) { pIrqObject[ 7]->CallHandlers(); } void far ISR09( void ) { pIrqObject[ 9]->CallHandlers(); } void far ISR10( void ) { pIrqObject[10]->CallHandlers(); } void far ISR11( void ) { pIrqObject[11]->CallHandlers(); } void far ISR12( void ) { pIrqObject[12]->CallHandlers(); } void far ISR13( void ) { pIrqObject[13]->CallHandlers(); } void far ISR14( void ) { pIrqObject[14]->CallHandlers(); } void far ISR15( void ) { pIrqObject[15]->CallHandlers(); } // 13 Jun 07 MF void far ISR16( void ) { pIrqObject[16]->CallHandlers(); } void far ISR17( void ) { pIrqObject[17]->CallHandlers(); } void far ISR18( void ) { pIrqObject[18]->CallHandlers(); } void far ISR19( void ) { pIrqObject[19]->CallHandlers(); } void far ISR20( void ) { pIrqObject[20]->CallHandlers(); } void far ISR21( void ) { pIrqObject[21]->CallHandlers(); } void far ISR22( void ) { pIrqObject[22]->CallHandlers(); } void far ISR23( void ) { pIrqObject[23]->CallHandlers(); } // List of handlers here. typedef void (NEAR *pfnISR)(void); static pfnISR pISR[NumIrqLevels] = { NULL, NULL, NULL, (pfnISR) ISR03, (pfnISR) ISR04, (pfnISR) ISR05, NULL, (pfnISR) ISR07, NULL, (pfnISR) ISR09, (pfnISR) ISR10, (pfnISR) ISR11, (pfnISR) ISR12, (pfnISR) ISR13, (pfnISR) ISR14, (pfnISR) ISR15, // 13 Jun 07 MF (pfnISR) ISR16, (pfnISR) ISR17, (pfnISR) ISR18, (pfnISR) ISR19, (pfnISR) ISR20, (pfnISR) ISR21, (pfnISR) ISR22, (pfnISR) ISR23 }; /**@external pMakeIRQ * Returns a pointer to the IRQ object which manages the IRQ for a given * level. * @param unsigned irq_level - the irq level of interest. * @return IRQ* pIRQ - pointer to the IRQ object for named level. * @notes This routine makes sure that we make exactly one IRQ for an * interrupt level. */ IRQ* pMakeIRQ( unsigned irq_level ) { if ( ! pIrqObject[irq_level] ) new IRQ( irq_level ); return pIrqObject[ irq_level ]; } IRQ* getIRQ( unsigned irq_level ) { if ( ! pIrqObject[irq_level] ) return 0; return pIrqObject[ irq_level ]; } /**@internal IRQ::IRQ * Constructor for an IRQ object. * @notes This method should never be called by a client, the pMakeIRQ() * function should be used instead. pMakeIRQ ensures that we have no * more than one IRQ object per level. */ IRQ::IRQ( unsigned irq_level ) { // 13 Jun 07 SHL _fmemset( (PVOID) this, 0, sizeof(IRQ) ); // Set all data to 0. _usIRQLevel = irq_level; pIrqObject[ _usIRQLevel ] = this; } IRQ::~IRQ(void) { ISR_Entry* pEntry; // Pointer into array of int handlers for this IRQ. // Remove object from global array. pIrqObject[ _usIRQLevel ] = NULL; // Remove all registered handlers. Quit when find NULL // entry. Array is guaranteed to be terminated with a NULL entry. for ( pEntry=_handlerList; pEntry->pfnHandler; ++pEntry ) bRemoveHandler( pEntry->pfnHandler ); } BOOL IRQ::bAddHandler( PFN_InterruptHandler pfn ) { ISR_Entry* pEntry; // Pointer into array of int handlers for this IRQ. // If the handler is already registered on this level, done. if ( _pFindHandler( pfn ) ) return TRUE; pEntry = _pFindHandler( NULL ); // Find an empty slot. if (! pEntry) return FALSE; // None available. // Set up new slot. No serialization needed, entry is disabled. pEntry->bEnabled = FALSE; // Sanity check - ensure entry is disabled. pEntry->pfnHandler = pfn; // Save pointer to handler. return TRUE; // Done. } BOOL IRQ::bEnableHandler ( PFN_InterruptHandler pfn ) { ISR_Entry* pEntry; // Pointer into array of int handlers for this IRQ. pEntry = _pFindHandler( pfn ); if (! pEntry) return FALSE; // Can't find it. if ( pEntry->bEnabled ) // Already running. return TRUE; pEntry->bEnabled = TRUE; // Enable handler. ++ _nEnabled; // Bump count of active handlers. // If we just changed from none enabled to 1 enabled, turn on Interrupt. // SvL: TODO: DOES NOT WORK FOR ISA CARDS!!!!!!!!!! (shared irq) if ( _nEnabled == 1 ) { USHORT uResult = DevHelp_SetIRQ( (NPFN) pISR[ _usIRQLevel ], _usIRQLevel, 1 ); // first try shared shared if (uResult != 0) { // If error ... uResult = DevHelp_SetIRQ( (NPFN) pISR[ _usIRQLevel ], _usIRQLevel, 0 ); // failed, so try exclusive instead if (uResult != 0) { // If error ... -- _nEnabled; pEntry->bEnabled = FALSE; return FALSE; } } } return TRUE; } BOOL IRQ::bDisableHandler ( PFN_InterruptHandler pfn ) { ISR_Entry* pEntry; // Pointer into array of int handlers for this IRQ. pEntry = _pFindHandler( pfn ); if (! pEntry) return FALSE; // Can't find it. if (! pEntry->bEnabled ) // Already disabled. return TRUE; pEntry->bEnabled = FALSE; // Disable handler. -- _nEnabled; // Bump down count of active handlers. // If we just changed from 1 enabled to 0 enabled, turn off Interrupt. if ( _nEnabled == 0 ) DevHelp_UnSetIRQ( _usIRQLevel ); return TRUE; } BOOL IRQ::bRemoveHandler( PFN_InterruptHandler pfn ) { ISR_Entry* pEntry; // Pointer into array of int handlers for this IRQ. ISR_Entry* pLastEntry; // Points to last handler entry in array. pEntry = _pFindHandler( pfn ); if (! pEntry) return FALSE; if (! bDisableHandler( pfn )) return FALSE; /* If this is the last entry in the array, just zero the slot. If this ; entry is in the middle of the array, then move the last entry in the ; list into this slot that is being freed. We guarantee that the ISR's ; are contiguous in the array and cannot put a NULL entry in the middle. */ // Find last entry in the array. for ( pLastEntry=pEntry; pLastEntry->pfnHandler; ++pLastEntry ) ; --pLastEntry; // If 'pfn' wasn't at end of the list, move end of list into this slot. if ( pLastEntry != pEntry) { // If we don't cli/sti here, we could lose an interrupt. cli(); *pEntry = *pLastEntry; // Copy down entry. pLastEntry->bEnabled = 0; // Disable last slot. pLastEntry->pfnHandler = NULL; // Zero out last slot. sti(); } else { // Else the entry selected for deletion is the // last entry in list; no need to move anything. pEntry->bEnabled = FALSE; // Disable this slot (redundant to DisableHandler). pEntry->pfnHandler = NULL; // Zero out the slot. } return TRUE; } ISR_Entry* IRQ::_pFindHandler( PFN_InterruptHandler pfn ) /* ; Finds named handler in __handlerList[]. Returns pointer to ISR_Entry ; if found, NULL otherwise. */ { ISR_Entry* pEntry; // Pointer into array of int handlers for this IRQ. // Walk array, looking for named handler function. for ( pEntry=_handlerList; pEntry != &_handlerList[MAX_HandlersPerIRQLevel]; ++pEntry ) if ( pfn == pEntry->pfnHandler ) return pEntry; // If we got here, we didn't find the entry. Return NULL. return NULL; } void IRQ::CallHandlers ( void ) /* ; Receives interrupt from ISR, then calls out to registered handlers. ; Runs in interrupt context. */ { USHORT i; // Indexes through ISR's in _handlerList. USHORT retry; // Retry count. for (retry=0; retry < _nEnabled; ++retry) { // Call all enabled handlers. Stop when find NULL entry (end of list). for ( i=0; _handlerList[i].pfnHandler; ++i ) { if ( _handlerList[i].bEnabled ) { // If handler enabled... if (_handlerList[i].pfnHandler(_usIRQLevel)) { // Call handler. cli(); // We've cleared all service requests. Send EOI and clear // the carry flag (tells OS/2 kernel that Int was handled). DevHelp_EOI( _usIRQLevel ); clc(); sti(); return; } } } } // Indicate Interrupt not serviced by setting carry flag before // returning to OS/2 kernel. OS/2 will then shut down the interrupt! // NOTE: Make sure interrupts are not turned on again when this irq isn't ours! stc(); }