windows-nt/Source/XPSP1/NT/drivers/wdm/bda/samples/mauitune/i2script.cpp
2020-09-26 16:20:57 +08:00

1358 lines
42 KiB
C++

//==========================================================================;
//
// 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 <wdm.h>
}
#include <unknown.h>
#include "ks.h"
#include "ksmedia.h"
#include <ksdebug.h>
#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);
}