using System;
using System.Runtime.InteropServices;
using NvAPIWrapper.Native.Attributes;
using NvAPIWrapper.Native.General.Structures;
using NvAPIWrapper.Native.Helpers;
using NvAPIWrapper.Native.Helpers.Structures;
using NvAPIWrapper.Native.Interfaces;
using NvAPIWrapper.Native.Interfaces.GPU;
namespace NvAPIWrapper.Native.GPU.Structures
{
///
[StructLayout(LayoutKind.Sequential, Pack = 8)]
[StructureVersion(2)]
public struct I2CInfoV2 : IInitializable, IDisposable, II2CInfo
{
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly StructureVersion _Version;
private readonly OutputId _OutputMask;
private readonly byte _UseDDCPort;
private readonly byte _I2CDeviceAddress;
private ValueTypeArray _I2CRegisterAddress;
private readonly uint _I2CRegisterAddressLength;
private ValueTypeArray _Data;
private readonly uint _DataLength;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly uint _I2CSpeed;
private readonly I2CSpeed _I2CSpeedInKHz;
///
// ReSharper disable once ConvertToAutoProperty
public OutputId OutputMask
{
get => _OutputMask;
}
///
public bool UseDDCPort
{
get => _UseDDCPort > 0;
}
///
// ReSharper disable once ConvertToAutoProperty
public I2CSpeed Speed
{
get => _I2CSpeedInKHz;
}
///
public bool IsReadOperation
{
get => (_I2CDeviceAddress & 1) == 1;
}
///
public byte DeviceAddress
{
get => (byte) (_I2CDeviceAddress >> 1);
}
///
public byte[] Data
{
get
{
if (_Data.IsNull || _DataLength == 0)
{
return new byte[0];
}
return _Data.ToArray((int) _DataLength);
}
}
///
public byte[] RegisterAddress
{
get
{
if (_I2CRegisterAddress.IsNull || _I2CRegisterAddressLength == 0)
{
return new byte[0];
}
return _I2CRegisterAddress.ToArray((int) _I2CRegisterAddressLength);
}
}
///
public byte? PortId
{
get => null;
}
///
/// Creates an instance of for write operations.
///
/// The target display output mask
/// A boolean value indicating that the DDC port should be used instead of the communication port
/// The device I2C slave address
/// The target I2C register address
/// The payload data
/// The target speed of the transaction in kHz
public I2CInfoV2(
OutputId outputMask,
bool useDDCPort,
byte deviceAddress,
byte[] registerAddress,
byte[] data,
I2CSpeed speed = I2CSpeed.Default
) : this(outputMask, useDDCPort, deviceAddress, false, registerAddress, data, speed)
{
}
///
/// Creates an instance of for read operations.
///
/// The target display output mask
/// A boolean value indicating that the DDC port should be used instead of the communication port
/// The device I2C slave address
/// The target I2C register address
/// The length of the buffer to allocate for the read operation.
/// The target speed of the transaction in kHz
public I2CInfoV2(
OutputId outputMask,
bool useDDCPort,
byte deviceAddress,
byte[] registerAddress,
uint readDataLength,
I2CSpeed speed = I2CSpeed.Default
) : this(outputMask, useDDCPort, deviceAddress, true, registerAddress, new byte[readDataLength], speed)
{
}
private I2CInfoV2(
OutputId outputMask,
bool useDDCPort,
byte deviceAddress,
bool isRead,
byte[] registerAddress,
byte[] data,
I2CSpeed speed = I2CSpeed.Default
)
{
this = typeof(I2CInfoV2).Instantiate();
_UseDDCPort = useDDCPort ? (byte) 1 : (byte) 0;
_OutputMask = outputMask;
_I2CDeviceAddress = (byte) (deviceAddress << 1);
_I2CSpeed = 0xFFFF; // Deprecated
_I2CSpeedInKHz = speed;
if (isRead)
{
_I2CDeviceAddress |= 1;
}
if (registerAddress?.Length > 0)
{
_I2CRegisterAddress = ValueTypeArray.FromArray(registerAddress);
_I2CRegisterAddressLength = (uint) registerAddress.Length;
}
else
{
_I2CRegisterAddress = ValueTypeArray.Null;
_I2CRegisterAddressLength = 0;
}
if (data?.Length > 0)
{
_Data = ValueTypeArray.FromArray(data);
_DataLength = (uint) data.Length;
}
else
{
_Data = ValueTypeArray.Null;
_DataLength = 0;
}
}
///
/// Calculates and fills the last byte of data to the checksum value required by the DDCCI protocol
///
/// The target device address.
/// The target register address.
/// The data to be sent and store the checksum.
public static void FillDDCCIChecksum(byte deviceAddress, byte[] registerAddress, byte[] data)
{
var checksum = deviceAddress;
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (data.Length == 0)
{
throw new ArgumentException("Checksum needs at least one free byte.", nameof(data));
}
if (registerAddress == null)
{
throw new ArgumentNullException(nameof(registerAddress));
}
// ReSharper disable once ForCanBeConvertedToForeach
// ReSharper disable once LoopCanBeConvertedToQuery
for (var i = 0; i < registerAddress.Length; i++)
{
checksum ^= registerAddress[i];
}
// ReSharper disable once ForCanBeConvertedToForeach
// ReSharper disable once LoopCanBeConvertedToQuery
for (var i = 0; i < data.Length - 1; i++)
{
checksum ^= data[i];
}
data[data.Length - 1] = checksum;
}
///
public void Dispose()
{
if (!_I2CRegisterAddress.IsNull)
{
_I2CRegisterAddress.Dispose();
}
if (!_Data.IsNull)
{
_Data.Dispose();
}
}
}
}