I'm doing a IO Control function call to set a I2C device's frequency as the
first part of a sequence of other IO calls. I'm getting an access violation
in unmanged code in passing PBYTE pBufIn in the function below's signature.
I've included a description of the comments for the driver function call and
the application user code I wrote.
Can someone tell me what I didn't do or tell me how to code this correctly
so it passs the PBYTE pBufin argument that represents the frequency value.
It is defined as a 4 byte array and the driver code casts this to a DWORD.
The frequency value is not being set
--Device Code ---
//
// Function: I2C_IOControl
//
// This function sends a command to a device.
//
// Parameters:
// hOpenContext
// [in] Handle to the open context of the device. The XXX_Open
// function creates and returns this identifier.
// dwCode
// [in] I/O control operation to perform. These codes are
// device-specific and are usually exposed to developers
through
// a header file.
// pBufIn
// [in] Pointer to the buffer containing data to transfer to the
// device.
// dwLenIn
// [in] Number of bytes of data in the buffer specified for pBufIn.
//
// pBufOut
// [out] Pointer to the buffer used to transfer the output data
// from the device.
// dwLenOut
// [in] Maximum number of bytes in the buffer specified by pBufOut.
//
// pdwActualOut
// [out] Pointer to the DWORD buffer that this function uses to
// return the actual number of bytes received from the
device.
//
// Returns:
// The new data pointer for the device indicates success. A value of -1
// indicates failure.
BOOL I2C_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn,
DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
PDWORD pdwActualOut)
{
BOOL bRet = FALSE;
// hOpenContext is a pointer to I2CClass instance!
I2CClass* pI2C = (I2CClass*) hOpenContext;
DEBUGMSG (ZONE_IOCTL|ZONE_FUNCTION, (TEXT("I2C_IOControl:
+hOpenContext=0x%x\r\n"),hOpenContext));
if (pI2C != NULL)
{
switch (dwCode)
{
case I2C_IOCTL_SET_FREQUENCY:
{
if (dwLenIn != sizeof(DWORD))
return FALSE;
PDWORD pdwFrequency = (PDWORD) MapCallerPtr(pBufIn,
sizeof(DWORD));
DEBUGMSG (ZONE_IOCTL|ZONE_FUNCTION,
(TEXT("I2C_IOControl:SET_FREQUENCY + ValIn=0x%x\r\n"), *pdwFrequency));
WORD wClkRate = BSPCalculateClkRateDiv(*pdwFrequency);
pI2C->SetClockRateDivider(wClkRate);
bRet = TRUE;
break;
}
default:
{
bRet = FALSE;
break;
}
}
}
---Application User Code----
//
// IOCTL codes
//
public static uint CTL_CODE(uint DeviceType, uint Function, uint
Method, uint Access)
{
return(((DeviceType) << 16) | ((Access) << 14) | ((Function) <<
2) | (Method));
}
//
// Set the Frequency of I2C device
//
Byte[] freq = new Byte[4];
Byte[] ba = BitConverter.GetBytes(i2CBus.I2C_SCLK_FREQ);
freq[0] = ba[3];
freq[1] = ba[2];
freq[2] = ba[1];
freq[3] = ba[0];
GCHandle handle = GCHandle.Alloc(freq, GCHandleType.Pinned);
IntPtr p = handle.AddrOfPinnedObject();
uint objectsize = freq.Length;
bool val = I2C.I2CIOControl(m_hI2c,
i2CBus.CTL_CODE(i2CBus.FILE_DEVICE_BUS_EXTENDER,
i2CBus.I2C_IOCTL_SET_FREQUENCY, i2CBus.METHOD_BUFFERED,
i2CBus.FILE_ANY_ACCESS), p, objectsize, IntPtr.Zero, 0, IntPtr.Zero);
Marshal.FreeHGlobal(p);
Ben Voigt [C++ MVP] - 09 Jun 2007 18:32 GMT
> I'm doing a IO Control function call to set a I2C device's frequency as
> the
[quoted text clipped - 10 lines]
> It is defined as a 4 byte array and the driver code casts this to a DWORD.
> The frequency value is not being set
Then you should declare the argument as "ref uint", which is the C#
equivalent to DWORD.
For low-level interaction with device drivers, though, I've always found
that C++/CLI is far superior to C#.
Just something like:
#include <winnt.h>
ref class PWM
{
public:
property uint Frequency
{
void set(uint value);
}
};
void PWM::Frequency::set(uint value)
{
if (!I2CIOControl(m_hI2c, I2C_IOCTL_SET_FREQUENCY, &value, sizeof value,
nullptr, 0, nullptr))
throw gcnew IOException("Failed to set PWM frequency to " +
value.ToString());
}
> --Device Code ---
> //
[quoted text clipped - 105 lines]
>
> Marshal.FreeHGlobal(p);
Ben Voigt [C++ MVP] - 09 Jun 2007 18:47 GMT
>> I'm doing a IO Control function call to set a I2C device's frequency as
>> the
[quoted text clipped - 32 lines]
> }
> };
Of course you should connect to the driver in the PWM constructor, and
implement a destructor that closes it properly (C++ will translate your
destructor into an implementation of IDisposable).
> void PWM::Frequency::set(uint value)
> {
[quoted text clipped - 114 lines]
>>
>> Marshal.FreeHGlobal(p);