//==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992 - 1996 Microsoft Corporation. All Rights Reserved. // // I2CSCRPT.C // I2CScript class implementation. // Main Include Module. // //==========================================================================; extern "C" { #include } #include #include "ks.h" #include "ksmedia.h" #include #include "i2script.h" #include "wdmdebug.h" //$REVIEW - Let's find a way to get the proper module name into this // #define MODULENAME "PhilTune" #define MODULENAMEUNICODE L"PhilTune" #define STR_MODULENAME MODULENAME #define ENSURE do #define END_ENSURE while( FALSE) #define FAIL break /*^^* * CI2CScript() * Purpose : CI2CScript class constructor. * Performs checking of the I2C provider presence. Sets the script in the initial state. * * Inputs : PUINT puiError : pointer to return a completion error code * PHW_STREAM_REQUEST_BLOCK pSrb : pointer to HW_INITIALIZE SRB * * Outputs : none * Author : IKLEBANOV *^^*/ CI2CScript:: CI2CScript( PDEVICE_OBJECT pDeviceObject, NTSTATUS * pStatus ) { *pStatus = STATUS_SUCCESS; m_dwI2CAccessKey = 0; m_liOperationStartTime.QuadPart = 0; m_i2cProviderInterface.i2cOpen = NULL; m_i2cProviderInterface.i2cAccess = NULL; m_pdoDriver = NULL; if( !InitializeAttachI2CProvider( &m_i2cProviderInterface, pDeviceObject)) { *pStatus = STATUS_NOINTERFACE; } else { // there was no error to get I2CInterface from the MiniVDD m_pdoDriver = pDeviceObject; m_ulI2CAccessClockRate = I2C_FIXED_CLOCK_RATE; } _DbgPrintF( DEBUGLVL_VERBOSE, ( "CI2CScript:CI2CScript() exit Error = %x\n", * pStatus) ); } /*^^* * LockI2CProvider() * Purpose : locks the I2CProvider for exclusive use * * Inputs : none * * Outputs : BOOL : retunrs TRUE, if the I2CProvider is locked * Author : IKLEBANOV *^^*/ BOOL CI2CScript::LockI2CProvider( void) { BOOL bResult; I2CControl i2cAccessBlock; bResult = FALSE; ENSURE { if(( m_i2cProviderInterface.i2cOpen == NULL) || ( m_i2cProviderInterface.i2cAccess == NULL) || ( m_pdoDriver == NULL)) FAIL; i2cAccessBlock.Status = I2C_STATUS_NOERROR; if( m_i2cProviderInterface.i2cOpen( m_pdoDriver, TRUE, &i2cAccessBlock) != STATUS_SUCCESS) { _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript: LockI2CProvider() bResult = %x\n", bResult) ); FAIL; } if( i2cAccessBlock.Status != I2C_STATUS_NOERROR) { _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript: LockI2CProvider() Status = %x\n", i2cAccessBlock.Status) ); FAIL; } // the I2C Provider has granted access - save dwCookie for further use m_dwI2CAccessKey = i2cAccessBlock.dwCookie; bResult = TRUE; } END_ENSURE; return( bResult); } /*^^* * LockI2CProvider() * Purpose : locks the I2CProvider for exclusive use. Provides attempts to lock the * provider unless either the time-out condition or the attempt succeeded. * * Inputs : none * * Outputs : BOOL : retunrs TRUE, if the I2CProvider is locked * Author : IKLEBANOV *^^*/ BOOL CI2CScript::LockI2CProviderEx( void) { LARGE_INTEGER liTime; m_liOperationStartTime.QuadPart = 0; while( !LockI2CProvider()) { KeQuerySystemTime( &liTime); if( m_liOperationStartTime.QuadPart) m_liOperationStartTime.QuadPart = liTime.QuadPart; else if( liTime.QuadPart - m_liOperationStartTime.QuadPart > I2CSCRIPT_TIMELIMIT_OPENPROVIDER) { // the time is expired - abort the initialization return( FALSE); } liTime.QuadPart = I2CSCRIPT_DELAY_OPENPROVIDER; KeDelayExecutionThread( KernelMode, FALSE, &liTime); } return( TRUE); } /*^^* * GetI2CProviderLockStatus() * Purpose : retrieves I2CProvider lock status * * Inputs : none * * Outputs : BOOL : retunrs TRUE, if the I2CProvider has been locked * Author : IKLEBANOV *^^*/ BOOL CI2CScript::GetI2CProviderLockStatus( void) { return( m_dwI2CAccessKey); } /*^^* * ReleaseI2CProvider() * Purpose : releases the I2CProvider for other clients' use * * Inputs : none * * Outputs : BOOL : retunrs TRUE, if the I2CProvider is released * Author : IKLEBANOV *^^*/ BOOL CI2CScript::ReleaseI2CProvider( void) { BOOL bResult; I2CControl i2cAccessBlock; bResult = FALSE; ENSURE { if(( m_i2cProviderInterface.i2cOpen == NULL) || ( m_i2cProviderInterface.i2cAccess == NULL) || ( m_pdoDriver == NULL)) // the I2CProvider was not found FAIL; i2cAccessBlock.Status = I2C_STATUS_NOERROR; i2cAccessBlock.dwCookie = m_dwI2CAccessKey; i2cAccessBlock.ClockRate = m_ulI2CAccessClockRate; if( m_i2cProviderInterface.i2cOpen( m_pdoDriver, FALSE, &i2cAccessBlock) != STATUS_SUCCESS) { _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript: ReleaseI2CProvider() bResult = %x\n", bResult) ); FAIL; } if( i2cAccessBlock.Status != I2C_STATUS_NOERROR) { _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript: ReleaseI2CProvider() bResult = %x\n", bResult) ); FAIL; } m_dwI2CAccessKey = 0; bResult = TRUE; } END_ENSURE; return( bResult); } /*^^* * PerformI2CPacketOperation() * Purpose : synchronosly executes I2C access packet. It assumed to be executed at Low priority. * The function does not return until the I2C session is done. The execution * is not dependent on the I2C Provider lock status * * Inputs : PI2CPacket pI2CPacket : pointer to I2C access packet * * Outputs : BOOL : returns TRUE, if I2C operation was carried out successfuly * The error status is returned via uchI2CResult field of the PI2CPacket * Author : IKLEBANOV *^^*/ BOOL CI2CScript::PerformI2CPacketOperation( IN OUT PI2CPacket pI2CPacket) { BOOL bResult; if( GetI2CProviderLockStatus()) // the Provider was locked before and we're not going to change it bResult = ExecuteI2CPacket( pI2CPacket); else { // the Provider was not locked and it's our responsibility to lock it first, // execute I2C operation and release it after the use if( LockI2CProviderEx()) { bResult = ExecuteI2CPacket( pI2CPacket); ReleaseI2CProvider(); } else bResult = FALSE; } return( bResult); } /*^^* * ExecuteI2CPacket() * Purpose : synchronosly executes I2C access packet. It assumed to be executed at Low priority. * The function does not return until the I2C session is done. This kind of access * is used during initialization ( boot up) time only. This function should be * called only after the I2CProvider was locked for exclusive service * * Inputs : PI2CPacket pI2CPacket : pointer to I2C access packet * * Outputs : BOOL : returns TRUE, if I2C operation was carried out successfuly * The error status is returned via uchI2CResult field of the PI2CPacket * Author : IKLEBANOV *^^*/ BOOL CI2CScript::ExecuteI2CPacket( IN OUT PI2CPacket pI2CPacket) { UINT nError, cbCount; UCHAR uchValue; UCHAR uchI2CResult = I2C_STATUS_ERROR; ENSURE { I2CControl i2cAccessBlock; if(( nError = CheckI2CScriptPacket( pI2CPacket)) != I2CSCRIPT_NOERROR) FAIL; // we'll use I2CProvider interface, assuming there is a syncronous provider // for asynchronous provider some work has to be added. 16 bits emulation is // not supported at this time either. This implementation does not support // Read-Modify-Write request either ENSURE { UINT nIndex; i2cAccessBlock.dwCookie = m_dwI2CAccessKey; i2cAccessBlock.ClockRate = m_ulI2CAccessClockRate; // We assume the last byte in the buffer belongs to the Write operation // after Read-Modify, is specified. cbCount = ( pI2CPacket->usFlags & I2COPERATION_READWRITE) ? ( pI2CPacket->cbWriteCount - 1) : ( pI2CPacket->cbWriteCount); if( cbCount) { // implement a write request // apply START condition with the I2C chip address first i2cAccessBlock.Flags = I2C_FLAGS_START | I2C_FLAGS_ACK; i2cAccessBlock.Command = I2C_COMMAND_WRITE; i2cAccessBlock.Data = pI2CPacket->uchChipAddress & 0xFE; if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) != I2C_STATUS_NOERROR) FAIL; i2cAccessBlock.Flags = I2C_FLAGS_ACK; for( nIndex = 0; nIndex < cbCount; nIndex ++) { // write the data from the buffer i2cAccessBlock.Data = pI2CPacket->puchWriteBuffer[nIndex]; if(( nIndex == cbCount - 1) && !( pI2CPacket->usFlags & I2COPERATION_RANDOMACCESS)) // the last byte to write - apply STOP condition, if no // I2COPERATION_RANDOMACCESS flag is specified i2cAccessBlock.Flags |= I2C_FLAGS_STOP; if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) != I2C_STATUS_NOERROR) break; } if( nIndex != cbCount) FAIL; /* // STOP condition is applied withe the last byte to be written // apply stop condition as the end of write operation i2cAccessBlock.Flags = I2C_FLAGS_STOP; i2cAccessBlock.Command = I2C_COMMAND_NULL; m_i2cProviderInterface.i2cAccess( m_pdoDriver, &i2cAccessBlock); */ } if( pI2CPacket->cbReadCount) { // implement a read request // apply START condition with the I2C chip address first i2cAccessBlock.Flags = I2C_FLAGS_START | I2C_FLAGS_ACK; i2cAccessBlock.Command = I2C_COMMAND_WRITE; i2cAccessBlock.Data = pI2CPacket->uchChipAddress | 0x01; if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) != I2C_STATUS_NOERROR) FAIL; i2cAccessBlock.Flags = I2C_FLAGS_ACK; i2cAccessBlock.Command = I2C_COMMAND_READ; for( nIndex = 0; nIndex < pI2CPacket->cbReadCount; nIndex ++) { // read the data to the buffer if( nIndex == ( UINT)( pI2CPacket->cbReadCount - 1)) { // don't apply ACK at the last read - read operation termination i2cAccessBlock.Flags &= ~I2C_FLAGS_ACK; // also apply STOP condition for the last byte i2cAccessBlock.Flags |= I2C_FLAGS_STOP; } if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) != I2C_STATUS_NOERROR) break; pI2CPacket->puchReadBuffer[nIndex] = i2cAccessBlock.Data; } if( nIndex != pI2CPacket->cbReadCount) FAIL; /* // STOP condition is applied with the last byte to be read // apply stop condition as the end of read operation i2cAccessBlock.Flags = I2C_FLAGS_STOP; i2cAccessBlock.Command = I2C_COMMAND_NULL; m_i2cProviderInterface.i2cAccess( m_pdoDriver, &i2cAccessBlock); */ if( pI2CPacket->usFlags & I2COPERATION_READWRITE) { // write operation should be taken care again, the last byte in the pbyWriteBuffer // should be constructed from the value read back and the binary operations OR and AND // with the values specified in the packet uchValue = pI2CPacket->puchReadBuffer[pI2CPacket->cbReadCount - 1]; uchValue &= pI2CPacket->uchANDValue; pI2CPacket->puchWriteBuffer[pI2CPacket->cbWriteCount - 1] = uchValue | pI2CPacket->uchORValue; if( pI2CPacket->cbWriteCount) { // implement a write request // apply START condition with the I2C chip address first i2cAccessBlock.Flags = I2C_FLAGS_START | I2C_FLAGS_ACK; i2cAccessBlock.Command = I2C_COMMAND_WRITE; i2cAccessBlock.Data = pI2CPacket->uchChipAddress & 0xFE; if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) != I2C_STATUS_NOERROR) FAIL; i2cAccessBlock.Flags = I2C_FLAGS_ACK; for( nIndex = 0; nIndex < pI2CPacket->cbWriteCount; nIndex ++) { // write the data from the buffer i2cAccessBlock.Data = pI2CPacket->puchWriteBuffer[nIndex]; if( nIndex == ( UINT)( pI2CPacket->cbWriteCount - 1)) // the last byte to write - apply STOP condition i2cAccessBlock.Flags |= I2C_FLAGS_STOP; if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) != I2C_STATUS_NOERROR) break; } if( nIndex != pI2CPacket->cbWriteCount) FAIL; /* // STOP condition is applied withe the last byte to be written // apply stop condition as the end of write operation i2cAccessBlock.Flags = I2C_FLAGS_STOP; i2cAccessBlock.Command = I2C_COMMAND_NULL; m_i2cProviderInterface.i2cAccess( m_pdoDriver, &i2cAccessBlock); */ } } } uchI2CResult = I2C_STATUS_NOERROR; } END_ENSURE; if( uchI2CResult == I2C_STATUS_ERROR) { // there was an error during accessing I2C - issue Reset command i2cAccessBlock.Command = I2C_COMMAND_RESET; AccessI2CProvider( m_pdoDriver, &i2cAccessBlock); } pI2CPacket->uchI2CResult = uchI2CResult; return( TRUE); } END_ENSURE; _DbgPrintF( DEBUGLVL_VERBOSE, ( "CI2CScript:ExecuteI2CPacket() nError = %x", nError) ); return( FALSE); } /*^^* * CheckI2CScriptPacket() * Purpose : checks integrity of the I2C control package * * Inputs : PI2CPacket pI2CPacket : pointer to I2C access packet * * Outputs : BOOL : returns TRUE, if I2C control package is a valid one * * Author : IKLEBANOV *^^*/ UINT CI2CScript::CheckI2CScriptPacket( IN PI2CPacket pI2CPacket) { UINT nPacketError; ENSURE { if(( m_i2cProviderInterface.i2cOpen == NULL) || ( m_i2cProviderInterface.i2cAccess == NULL) || ( m_pdoDriver == NULL)) { // the I2CProvider was not found nPacketError = I2CSCRIPT_ERROR_NOPROVIDER; FAIL; } if(( !pI2CPacket->cbWriteCount) && ( !pI2CPacket->cbReadCount)) { // nothing to do nPacketError = I2CSCRIPT_ERROR_NODATA; FAIL; } if((( pI2CPacket->cbWriteCount) && ( pI2CPacket->puchWriteBuffer == NULL)) || (( pI2CPacket->cbReadCount) && ( pI2CPacket->puchReadBuffer == NULL))) { // NULL pointer, when the data is specified nPacketError = I2CSCRIPT_ERROR_NOBUFFER; FAIL; } if(( pI2CPacket->usFlags & I2COPERATION_READWRITE) && ( !pI2CPacket->cbWriteCount)) { // if Read-Modify-Write is specified, the Write data should be present nPacketError = I2CSCRIPT_ERROR_READWRITE; FAIL; } nPacketError = I2CSCRIPT_NOERROR; } END_ENSURE; return( nPacketError); } /*^^* * ClearScript() * Purpose : clears I2CScript to the NULL state - no I2C operations are on hold. * * Inputs : none * * Outputs : none * Author : IKLEBANOV *^^*/ void CI2CScript::ClearScript( void) { m_nExecutionIndex = 0; m_nScriptLength = 0; m_pfnReturnWhenDone = NULL; m_bExecutionInProcess = FALSE; } /*^^* * AppendToScript() * Purpose : appends a I2CPacket to the bottom of the I2CScript. * The 16 bits emulation is not implemented at this time. * * Inputs : PI2CPacket pI2CPacket - pointer to the I2C packet to append * * Outputs : BOOL : returns TRUE, if the packet was successfully appended. * FALSE might happend if the I2CPacket is a bad one, or overflow occurs * Author : IKLEBANOV *^^*/ BOOL CI2CScript::AppendToScript( PI2CPacket pI2CPacket) { UINT nError, nScriptIndex; UINT nIndex, cbCount; ENSURE { PI2CScriptPrimitive pI2CPrimitive; if(( nError = CheckI2CScriptPacket( pI2CPacket)) != I2CSCRIPT_NOERROR) FAIL; nError = I2CSCRIPT_ERROR_OVERFLOW; // m_nExecutionIndex is used as a Script build index. We will work with a local copy of it // first to ensure we have no overflow nScriptIndex = m_nExecutionIndex; pI2CPrimitive = &m_i2cScript[nScriptIndex]; // We assume the last byte in the buffer belongs to the Write operation // after Read-Modify, is specified. cbCount = ( pI2CPacket->usFlags & I2COPERATION_READWRITE) ? \ ( pI2CPacket->cbWriteCount - 1) : ( pI2CPacket->cbWriteCount); if( cbCount) { // I2C Chip address should be taken care of first pI2CPrimitive->ulCommand = I2C_COMMAND_WRITE; pI2CPrimitive->byData = pI2CPacket->uchChipAddress; pI2CPrimitive->byANDData = 0xFE; pI2CPrimitive->byORData = 0x00; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_START | I2C_FLAGS_ACK; pI2CPrimitive->byFlags = 0x0; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; pI2CPrimitive ++; // I2C write buffer should be taken care of. for( nIndex = 0; nIndex < cbCount; nIndex ++) { pI2CPrimitive->ulCommand = I2C_COMMAND_WRITE; pI2CPrimitive->byData = pI2CPacket->puchWriteBuffer[nIndex]; pI2CPrimitive->byORData = 0x00; pI2CPrimitive->byANDData = 0xFF; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_ACK; pI2CPrimitive->byFlags = 0x0; if( nIndex == cbCount - 1) // this is the last byte to be written - apply STOP pI2CPrimitive->ulProviderFlags |= I2C_FLAGS_STOP; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) break; pI2CPrimitive ++; } // check the Script length if( nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; /* // Stop condition is applied with the last byte to be written // We finished Write portion here, whether it's a Write only, Read-Modify-Write operation pI2CPrimitive->ulCommand = I2C_COMMAND_NULL; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_STOP; pI2CPrimitive->byFlags = 0x0; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; pI2CPrimitive ++; */ } // We have to see, if there is a Read operation involved if( pI2CPacket->cbReadCount) { // I2C Chip address should be taken care of first pI2CPrimitive->ulCommand = I2C_COMMAND_WRITE; pI2CPrimitive->byData = pI2CPacket->uchChipAddress; pI2CPrimitive->byANDData = 0xFE; pI2CPrimitive->byORData = 0x01; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_START | I2C_FLAGS_ACK; pI2CPrimitive->byFlags = 0x0; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; pI2CPrimitive ++; // I2C read buffer should be taken care of. We assume the last byte in the buffer belongs to // the Write operation after Read-Modify, is specified. for( nIndex = 0; nIndex < pI2CPacket->cbReadCount; nIndex ++) { pI2CPrimitive->ulCommand = I2C_COMMAND_READ; if( nIndex == ( UINT)( pI2CPacket->cbReadCount - 1)) { pI2CPrimitive->ulProviderFlags = I2C_FLAGS_STOP; pI2CPrimitive->byFlags = pI2CPacket->usFlags & I2COPERATION_READWRITE; } else { pI2CPrimitive->ulProviderFlags = I2C_FLAGS_ACK; pI2CPrimitive->byFlags = 0x0; } // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) break; pI2CPrimitive ++; } // check the Script length if( nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; /* // Stop condition is applied with the last byte to be read // We finished Read portion here, whether it's a Read only, Read-Modify-Write operation pI2CPrimitive->ulCommand = I2C_COMMAND_NULL; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_STOP; pI2CPrimitive->byFlags = 0x0; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; pI2CPrimitive ++; */ } // the last thing left to do, is to implement Write after Read-Modify, if specified if( pI2CPacket->usFlags & I2COPERATION_READWRITE) { // I2C Chip address should be taken care of first pI2CPrimitive->ulCommand = I2C_COMMAND_WRITE; pI2CPrimitive->byData = pI2CPacket->uchChipAddress; pI2CPrimitive->byANDData = 0xFE; pI2CPrimitive->byORData = 0x00; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_START | I2C_FLAGS_ACK; pI2CPrimitive->byFlags = 0x0; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; pI2CPrimitive ++; // I2C write buffer should be taken care of. for( nIndex = 0; nIndex < pI2CPacket->cbWriteCount; nIndex ++) { pI2CPrimitive->ulCommand = I2C_COMMAND_WRITE; pI2CPrimitive->byData = pI2CPacket->puchWriteBuffer[nIndex]; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_ACK; if( nIndex == ( UINT)( pI2CPacket->cbWriteCount - 1)) { // it's time to write the byte modified after the Read operation pI2CPrimitive->byORData = pI2CPacket->uchORValue; pI2CPrimitive->byANDData = pI2CPacket->uchANDValue; pI2CPrimitive->byFlags = I2COPERATION_READWRITE; // apply STOP condition with the last byte to be read pI2CPrimitive->ulProviderFlags |= I2C_FLAGS_STOP; } else { pI2CPrimitive->byORData = 0x00; pI2CPrimitive->byANDData = 0xFF; pI2CPrimitive->byFlags = 0x0; } // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) break; pI2CPrimitive ++; } // check the Script length if( nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; /* // Stop condition is applied with the last byte to be written // We finished Write portion here, whether it's a Write only, Read-Modify-Write operation pI2CPrimitive->ulCommand = I2C_COMMAND_NULL; pI2CPrimitive->ulProviderFlags = I2C_FLAGS_STOP; pI2CPrimitive->byFlags = 0x0; // check the Script length if( ++ nScriptIndex >= I2CSCRIPT_LENGTH_MAXIMUM) FAIL; pI2CPrimitive ++; */ } // the Packet was added succesfully to the Script. Modify the Script propertirs m_nExecutionIndex = nScriptIndex; m_nScriptLength = nScriptIndex; return( TRUE); } END_ENSURE; _DbgPrintF( DEBUGLVL_VERBOSE, ( "CI2CScript:AppendToScript() nError = %x", nError) ); return( FALSE); } /*^^* * ExecuteScript() * Purpose : triggers the execution of previously built I2CScript. This function is also * responsible for allocating I2CProvider for its own exclusive use. * * Inputs : PIRP pIrp * PHWCompletionRoutine pfnScriptCompletion: function pointer will be called, * when the script execution is completed. Indicates the Script execution * is to be carried out asynchronously. * * Outputs : BOOL : returns TRUE, if the execution was successfully triggered. * FALSE might happend if the Script has not been built by the time of the call * * Note : if pfnScriptExecuted is NULL pointer, the Script will be executed synchronously * * Author : IKLEBANOV *^^*/ BOOL CI2CScript::ExecuteScript( IN PIRP pIrp, IN PHWCompletionRoutine pfnScriptCompletion) { ENSURE { if( pfnScriptCompletion != NULL) // not supported at this point. The idea is to create a new system thread, // where the Script to be executed. When the Script will be copleted, // call-back is called and the thred terminates itself. FAIL; if( !m_nExecutionIndex) FAIL; // there is not a NULL Script - proceed m_nScriptLength = m_nExecutionIndex; m_nExecutionIndex = m_nCompletionIndex = 0; if( !GetI2CProviderLockStatus()) // The provider was not locked prior the Script execution if( !LockI2CProviderEx()) FAIL; InterpreterScript(); ReleaseI2CProvider(); return( TRUE); } END_ENSURE; return( FALSE); } /*^^* * InterpreterScript() * Purpose : interpreters the I2CScript line by line. The Script is not cleaned up * after the completion to allow to client retrive the results of * the script execution. It's the client responsibility to clean it up * upon the results retrieving * * Inputs : none * Outputs : none * * Author : IKLEBANOV *^^*/ void CI2CScript::InterpreterScript( void) { UINT nScriptIndex, nIndex; I2CControl i2cAccessBlock; m_bExecutionInProcess = TRUE; i2cAccessBlock.dwCookie = m_dwI2CAccessKey; i2cAccessBlock.ClockRate = m_ulI2CAccessClockRate; // We'll interpreter every line of the Script and call the I2C Provider to // execute it. It's assumed the I2CProvider is a syncronous one. If it's not the // case, the special care should be taken of based upon returned value I2C_STATUS_BUSY // in the Status. for( nScriptIndex = 0; nScriptIndex < m_nScriptLength; nScriptIndex ++) { i2cAccessBlock.Command = m_i2cScript[nScriptIndex].ulCommand; i2cAccessBlock.Flags = m_i2cScript[nScriptIndex].ulProviderFlags; if( i2cAccessBlock.Command == I2C_COMMAND_WRITE) { i2cAccessBlock.Data = m_i2cScript[nScriptIndex].byData; i2cAccessBlock.Data &= m_i2cScript[nScriptIndex].byANDData; i2cAccessBlock.Data |= m_i2cScript[nScriptIndex].byORData; } if( AccessI2CProvider( m_pdoDriver, &i2cAccessBlock) == I2C_STATUS_ERROR) break; // check, wether it's a Read operation - save result if( i2cAccessBlock.Command == I2C_COMMAND_READ) { m_i2cScript[nScriptIndex].byData = i2cAccessBlock.Data; // check, if this data belongs to Read-Modify-Write operation if( m_i2cScript[nScriptIndex].byFlags & I2COPERATION_READWRITE) { // let's look for the next I2COPERATION_READWRITE flag - it is the pair for( nIndex = nScriptIndex; nIndex < m_nScriptLength; nIndex ++) if(( m_i2cScript[nIndex].ulCommand == I2C_COMMAND_WRITE) && ( m_i2cScript[nIndex].byFlags & I2COPERATION_READWRITE)) break; if( nIndex >= m_nScriptLength) // the Script got corrupted break; m_i2cScript[nIndex].byData = i2cAccessBlock.Data; } } } m_nCompletionIndex = nScriptIndex; m_bExecutionInProcess = FALSE; } /*^^* * AccessI2CProvider() * Purpose : provide synchronous type of access to I2CProvider * * Inputs : PDEVICE_OBJECT pdoDriver : pointer to the client's device object * PI2CControl pi2cAccessBlock : pointer to a composed I2C access block * * Outputs : UINT : status of the I2C operation I2C_STATUS_NOERROR or I2C_STATUS_ERROR * * Author : IKLEBANOV *^^*/ UINT CI2CScript:: AccessI2CProvider( PDEVICE_OBJECT pdoClient, PI2CControl pi2cAccessBlock) { UINT uiStatus; LARGE_INTEGER liTime; do { // this loop is infinitive. It has to be taken care of ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL); if( m_i2cProviderInterface.i2cAccess( pdoClient, pi2cAccessBlock) != STATUS_SUCCESS) { uiStatus = I2C_STATUS_ERROR; break; } if( pi2cAccessBlock->Status != I2C_STATUS_BUSY) { uiStatus = pi2cAccessBlock->Status; break; } liTime.QuadPart = I2CSCRIPT_DELAY_GETPROVIDERSTATUS; ::KeDelayExecutionThread( KernelMode, FALSE, &liTime); pi2cAccessBlock->Command = I2C_COMMAND_STATUS; } while( TRUE); return( uiStatus); } /*^^* * GetScriptResults() * Purpose : returns result of the executed Script * This function idealy is called twice: * first time with the puchReadBuffer = NULL to retrive the number of bytes read * second time - to fill in the pointer * Inputs : PUINT puiReadCount : pointer to the counter of the bytes were read * PUCH puchReadBuffer : pointer to the buffer to put the data * * Outputs : UINT : status of the I2C operation * If the status is I2C_STATUS_ERROR, puiReadCount will contain the step, where * I2CScript failed * Author : IKLEBANOV *^^*/ UINT CI2CScript::GetScriptResults( PUINT puiReadCount, PUCHAR puchReadBuffer) { UINT nScriptIndex, nCount; ASSERT( puiReadCount != NULL); if( m_bExecutionInProcess) return( I2C_STATUS_BUSY); if( m_nScriptLength != m_nCompletionIndex) { // if the case of failure, step where I2CScript failed is return // instead of Read Counter. The returned status indicates the // failure * puiReadCount = m_nCompletionIndex; return( I2C_STATUS_ERROR); } else { nCount = 0; for( nScriptIndex = 0; nScriptIndex < m_nCompletionIndex; nScriptIndex ++) { if( m_i2cScript[nScriptIndex].ulCommand == I2C_COMMAND_READ) { if( puchReadBuffer != NULL) // fill in the supplied buffer puchReadBuffer[nCount] = m_i2cScript[nScriptIndex].byData; nCount ++; } } * puiReadCount = nCount; return( I2C_STATUS_NOERROR); } } /*^^* * InitializeAttachI2CProvider() * Purpose : gets the pointer to the parent I2C Provider interface using * several IRP_MJ_??? functions. * This function will be called at Low priority * * Inputs : I2CINTERFACE * pI2CInterface : pointer to the Interface to be filled in * PDEVICE_OBJECT pDeviceObject : MiniDriver device object, which is a child of I2C Master * * Outputs : BOOL - returns TRUE, if the interface was found * Author : IKLEBANOV *^^*/ BOOL CI2CScript::InitializeAttachI2CProvider( I2CINTERFACE * pI2CInterface, PDEVICE_OBJECT pDeviceObject) { BOOL bResult; // try the new path bResult = LocateAttachI2CProvider( pI2CInterface, pDeviceObject, IRP_MJ_PNP); if(( pI2CInterface->i2cOpen == NULL) || ( pI2CInterface->i2cAccess == NULL)) { // TRAP; _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript(): interface has NULL pointers\n") ); bResult = FALSE; } return( bResult); } /*^^* * LocateAttachI2CProvider() * Purpose : gets the pointer to the parent I2C Provider interface * This function will be called at Low priority * * Inputs : I2CINTERFACE * pI2CInterface : pointer to the Interface to be filled in * PDEVICE_OBJECT pDeviceObject : MiniDriver device object, which is a child of I2C Master * int nIrpMajorFunction : IRP major function to query the I2C Interface * * Outputs : BOOL - returns TRUE, if the interface was found * Author : IKLEBANOV *^^*/ BOOL CI2CScript::LocateAttachI2CProvider( I2CINTERFACE * pI2CInterface, PDEVICE_OBJECT pDeviceObject, int nIrpMajorFunction) { PIRP pIrp; BOOL bResult = FALSE; ENSURE { PIO_STACK_LOCATION pNextStack; NTSTATUS ntStatus; KEVENT Event; pIrp = IoAllocateIrp( pDeviceObject->StackSize, FALSE); if( pIrp == NULL) { // TRAP; _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript(): can not allocate IRP\n") ); FAIL; } pNextStack = IoGetNextIrpStackLocation( pIrp); if( pNextStack == NULL) { // TRAP; _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript(): can not allocate NextStack\n") ); FAIL; } //$REVIEW - Should change function decl to make nIrpMajorFunction be UCHAR - TCP pNextStack->MajorFunction = (UCHAR) nIrpMajorFunction; pNextStack->MinorFunction = IRP_MN_QUERY_INTERFACE; KeInitializeEvent( &Event, NotificationEvent, FALSE); IoSetCompletionRoutine( pIrp, I2CScriptIoSynchCompletionRoutine, &Event, TRUE, TRUE, TRUE); pNextStack->Parameters.QueryInterface.InterfaceType = ( struct _GUID *)&GUID_I2C_INTERFACE; pNextStack->Parameters.QueryInterface.Size = sizeof( I2CINTERFACE); pNextStack->Parameters.QueryInterface.Version = 1; pNextStack->Parameters.QueryInterface.Interface = ( PINTERFACE)pI2CInterface; pNextStack->Parameters.QueryInterface.InterfaceSpecificData = NULL; ntStatus = IoCallDriver( pDeviceObject, pIrp); if( ntStatus == STATUS_PENDING) KeWaitForSingleObject( &Event, Suspended, KernelMode, FALSE, NULL); if(( pI2CInterface->i2cOpen == NULL) || ( pI2CInterface->i2cAccess == NULL)) { _DbgPrintF( DEBUGLVL_ERROR, ( "CI2CScript(): interface has NULL pointers\n") ); FAIL; } bResult = TRUE; } END_ENSURE; if( pIrp != NULL) IoFreeIrp( pIrp); return( bResult); } /*^^* * I2CScriptIoSynchCompletionRoutine() * Purpose : This routine is for use with synchronous IRP processing. * All it does is signal an event, so the driver knows it and can continue. * * Inputs : PDEVICE_OBJECT DriverObject : Pointer to driver object created by system * PIRP pIrp : Irp that just completed * PVOID Event : Event we'll signal to say Irp is done * * Outputs : none * Author : IKLEBANOV *^^*/ extern "C" NTSTATUS I2CScriptIoSynchCompletionRoutine( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN PVOID Event) { KeSetEvent(( PKEVENT)Event, 0, FALSE); return( STATUS_MORE_PROCESSING_REQUIRED); } /* * WriteSeq() * Purpose : Write a specified sequence to address specified. * * * Inputs : UCHAR addr : I2CAddress * UCHAR *p_seq : sequence * USHORT len : length of the sequence * * Outputs : BOOL - TRUE if operation succeeded else FALSE * Author : MM * */ BOOL CI2CScript::WriteSeq(UCHAR addr, UCHAR *p_seq, USHORT len) { I2CPacket i2cPacket; LARGE_INTEGER liTime; // Init rest of I2CPacket i2cPacket.usFlags = I2COPERATION_WRITE; i2cPacket.uchChipAddress = addr; i2cPacket.cbReadCount = 0; i2cPacket.cbWriteCount = len; i2cPacket.puchReadBuffer = NULL; i2cPacket.puchWriteBuffer = p_seq; int counter = 0; // Somewhat arbitrary max of 1 second. while (!LockI2CProviderEx()) { if (counter++ >= 100) { // ReleaseI2CProvider(); return(FALSE); } liTime.QuadPart = 100000; KeDelayExecutionThread(KernelMode, FALSE, &liTime); // = 10 milliseconds } /* if(!CheckInterface(addr)) { ReleaseI2CProvider(); return FALSE; } */ ExecuteI2CPacket(&i2cPacket); ReleaseI2CProvider(); //DebugInfo(("CI2CScript::WriteSeq(): Inside\n")); if (i2cPacket.uchI2CResult != I2C_STATUS_NOERROR) { _DbgPrintF( DEBUGLVL_ERROR,("CI2CScript::WriteSeq(): Error\n")); return(FALSE); } return(TRUE); } /* * ReadSeq() * Purpose : Read a specified number of bytes from address specified. * * * Inputs : UCHAR addr : I2CAddress * UCHAR *p_seq : sequence * USHORT len : length of the sequence * * Outputs : BOOL - TRUE if operation succeeded else FALSE * Author : MM * * */ BOOL CI2CScript::ReadSeq(UCHAR addr, UCHAR *p_seq, USHORT len) { I2CPacket i2cPacket; LARGE_INTEGER liTime; // Init rest of I2CPacket i2cPacket.usFlags = I2COPERATION_READ; i2cPacket.uchChipAddress = addr; i2cPacket.cbReadCount = len; i2cPacket.cbWriteCount = 0; i2cPacket.puchReadBuffer = p_seq; i2cPacket.puchWriteBuffer = NULL; int counter = 0; // Somewhat arbitrary max of 1 second. while (!LockI2CProviderEx()) { if (counter++ >= 100) { // ReleaseI2CProvider(); return(FALSE); } liTime.QuadPart = 100000; KeDelayExecutionThread(KernelMode, FALSE, &liTime); // = 10 milliseconds } /* if(!CheckInterface(addr)) { ReleaseI2CProvider(); return FALSE; } */ ExecuteI2CPacket(&i2cPacket); ReleaseI2CProvider(); // DebugInfo(("CI2CScript::ReadSeq(): Inside\n")); if (i2cPacket.uchI2CResult != I2C_STATUS_NOERROR) { _DbgPrintF( DEBUGLVL_ERROR,("CI2CScript::ReadSeq(): Error\n")); return(FALSE); } return(TRUE); } /* * CombinedSeq() * Purpose : Write a specified sequence and then read a specified number of * bytes from address specified. * * * Inputs : UCHAR addr : I2CAddress * UCHAR seqWr[] : write sequence * USHORT lenWr : length of the write sequence * UCHAR seqRd[] : read sequence * USHORT lenRd : length of the read sequence * * Outputs : BOOL - TRUE if operation succeeded else FALSE * Author : MM * * */ BOOL CI2CScript::CombinedSeq(UCHAR addr, UCHAR seqWr[], USHORT lenWr, UCHAR seqRd[], USHORT lenRd) { I2CPacket i2cPacket; LARGE_INTEGER liTime; // Init rest of I2CPacket i2cPacket.usFlags = I2COPERATION_READWRITE; i2cPacket.uchChipAddress = addr; i2cPacket.cbReadCount = lenRd; i2cPacket.cbWriteCount = lenWr; i2cPacket.puchReadBuffer = seqRd; i2cPacket.puchWriteBuffer = seqWr; int counter = 0; // Somewhat arbitrary max of 1 second. while (!LockI2CProviderEx()) { if (counter++ >= 100) { // ReleaseI2CProvider(); return(FALSE); } liTime.QuadPart = 100000; KeDelayExecutionThread(KernelMode, FALSE, &liTime); // = 10 milliseconds } if(!CheckInterface(addr)) { ReleaseI2CProvider(); return FALSE; } ExecuteI2CPacket(&i2cPacket); ReleaseI2CProvider(); //DebugInfo(("CI2CScript::CombinedSeq(): Inside\n")); if (i2cPacket.uchI2CResult != I2C_STATUS_NOERROR) { return(FALSE); } return(TRUE); } /* * CheckInterface() * Purpose : Check if I2C Interface is working by reading a byte * from the specified address * * Inputs : UCHAR addr : I2CAddress * * Outputs : BOOL - TRUE if operation succeeded else FALSE * Author : MM * * */ BOOL CI2CScript::CheckInterface(UCHAR addr) { I2CPacket i2cPacket; LARGE_INTEGER liTime; UCHAR ucData; // Init rest of I2CPacket i2cPacket.usFlags = I2COPERATION_READ; i2cPacket.uchChipAddress = addr; i2cPacket.cbReadCount = 1; i2cPacket.cbWriteCount = 0; i2cPacket.puchReadBuffer = &ucData; i2cPacket.puchWriteBuffer = NULL; ExecuteI2CPacket(&i2cPacket); if (i2cPacket.uchI2CResult != I2C_STATUS_NOERROR) { _DbgPrintF( DEBUGLVL_ERROR,("CI2CScript::CheckInterface(): Error\n")); return(FALSE); } return(TRUE); }