You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1101 lines
38 KiB
1101 lines
38 KiB
//==========================================================================;
|
|
//
|
|
// 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"
|
|
{
|
|
#define INITGUID
|
|
|
|
#include "strmini.h"
|
|
#include "ksmedia.h"
|
|
#include "wdm.h"
|
|
|
|
#include "wdmdebug.h"
|
|
}
|
|
|
|
#include "i2script.h"
|
|
#include "wdmdrv.h"
|
|
|
|
|
|
/*^^*
|
|
* operator new
|
|
* Purpose : CI2CScript class overloaded operator new.
|
|
* Provides placement for a CI2CScript class object from the PADAPTER_DEVICE_EXTENSION
|
|
* allocated by the StreamClassDriver for the MiniDriver.
|
|
*
|
|
* Inputs : UINT size_t : size of the object to be placed
|
|
* PVOID pAllocation : casted pointer to the CI2CScript allocated data
|
|
*
|
|
* Outputs : PVOID : pointer of the CI2CScript class object
|
|
* Author : IKLEBANOV
|
|
*^^*/
|
|
PVOID CI2CScript::operator new( size_t stSize, PVOID pAllocation)
|
|
{
|
|
|
|
if( stSize != sizeof( CI2CScript))
|
|
{
|
|
OutputDebugError(( "CI2CScript: operator new() fails\n"));
|
|
return( NULL);
|
|
}
|
|
else
|
|
return( pAllocation);
|
|
}
|
|
|
|
|
|
|
|
/*^^*
|
|
* 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( PPORT_CONFIGURATION_INFORMATION pConfigInfo,
|
|
PUINT puiErrorCode)
|
|
{
|
|
m_dwI2CAccessKey = 0;
|
|
|
|
m_i2cProviderInterface.i2cOpen = NULL;
|
|
m_i2cProviderInterface.i2cAccess = NULL;
|
|
|
|
m_pdoDriver = NULL;
|
|
|
|
if( !InitializeAttachI2CProvider( &m_i2cProviderInterface, pConfigInfo->PhysicalDeviceObject))
|
|
* puiErrorCode = WDMMINI_ERROR_NOI2CPROVIDER;
|
|
else
|
|
{
|
|
// there was no error to get I2CInterface from the MiniVDD
|
|
m_pdoDriver = pConfigInfo->RealPhysicalDeviceObject;
|
|
m_ulI2CAccessClockRate = I2C_FIXED_CLOCK_RATE;
|
|
* puiErrorCode = WDMMINI_NOERROR;
|
|
}
|
|
|
|
OutputDebugTrace(( "CI2CScript:CI2CScript() exit Error = %x\n", * puiErrorCode));
|
|
}
|
|
|
|
|
|
|
|
/*^^*
|
|
* 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)
|
|
{
|
|
OutputDebugError(( "CI2CScript: LockI2CProvider() bResult = %x\n", bResult));
|
|
FAIL;
|
|
}
|
|
|
|
if( i2cAccessBlock.Status != I2C_STATUS_NOERROR)
|
|
{
|
|
OutputDebugError(( "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, liOperationStartTime;
|
|
|
|
liOperationStartTime.QuadPart = 0;
|
|
|
|
while( !LockI2CProvider())
|
|
{
|
|
KeQuerySystemTime( &liTime);
|
|
|
|
if( !liOperationStartTime.QuadPart)
|
|
liOperationStartTime.QuadPart = liTime.QuadPart;
|
|
else
|
|
if( liTime.QuadPart - 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)
|
|
{
|
|
OutputDebugError(( "CI2CScript: ReleaseI2CProvider() bResult = %x\n", bResult));
|
|
FAIL;
|
|
}
|
|
|
|
if( i2cAccessBlock.Status != I2C_STATUS_NOERROR)
|
|
{
|
|
OutputDebugError(( "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;
|
|
|
|
OutputDebugTrace(( "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;
|
|
|
|
OutputDebugTrace(( "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 : PHW_STREAM_REQUEST_BLOCK pSrb : pointer to the current SRB
|
|
* 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 PHW_STREAM_REQUEST_BLOCK pSrb,
|
|
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
|
|
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;
|
|
|
|
bResult = LocateAttachI2CProvider( pI2CInterface, pDeviceObject, IRP_MJ_PNP);
|
|
if(( pI2CInterface->i2cOpen == NULL) || ( pI2CInterface->i2cAccess == NULL))
|
|
{
|
|
TRAP;
|
|
OutputDebugError(( "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;
|
|
OutputDebugError(( "CI2CScript(): can not allocate IRP\n"));
|
|
FAIL;
|
|
}
|
|
|
|
pNextStack = IoGetNextIrpStackLocation( pIrp);
|
|
if( pNextStack == NULL)
|
|
{
|
|
TRAP;
|
|
OutputDebugError(( "CI2CScript(): can not allocate NextStack\n"));
|
|
FAIL;
|
|
}
|
|
|
|
pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
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))
|
|
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);
|
|
}
|