repo time
Dependencies: mbed MAX14720 MAX30205 USBDevice
Diff: HspGuiSourceV301/HSPGui/HID.cs
- Revision:
- 20:6d2af70c92ab
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HspGuiSourceV301/HSPGui/HID.cs Tue Apr 06 06:41:40 2021 +0000 @@ -0,0 +1,1788 @@ +using System; +using System.Collections; +using System.Threading; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.Win32.SafeHandles; + +//------------------------------------------------------------------------------------------ +// OS24EVK-59 split into HeartRateApp EXE and MAX30101 DLL. +// Moved all MAX30101 DLL classes into namespace Maxim.MAX30101GUI +// Moved all HeartRateApp GUI classes into namespace Maxim.MAX30101 +// OS24EVK-59 Create separate project that builds Maxim.MAX30101GUI DLL library + +// OS24EVK-59 moved class HID into namespace Maxim.MAX30101 instead of namespace HeartRateApp +namespace Maxim.MAX30101 +{ +#pragma warning disable 1574 + /// <summary> + /// USB Human Interface Device functions to connect to EV kit without requiring a custom device driver + /// </summary> +#pragma warning restore 1574 + public class HID + { + private IntPtr EventObject; + private System.Threading.NativeOverlapped managedOverlapped; + private IntPtr nonManagedOverlapped; + private IntPtr nonManagedBuffer; + public SafeFileHandle writeHandle; + public ArrayList writeHandleArray = new ArrayList(); + public SafeFileHandle readHandle; + public ArrayList readHandleArray = new ArrayList(); + public ArrayList desiredHIDPathNameArray = new ArrayList(); + public String deviceID = System.String.Format("Vid_{0:x4}&Pid_{1:x4}", support.DEFAULT_VENDOR_ID, support.DEFAULT_PRODUCT_ID); + + private bool explicit_report_id = true; + private const byte DEFAULT_REPORT_ID = 0; + private const byte SHORT_REPORT_ID = 1; + public const byte HID_REPORT_ID_1 = 1; // MAX30101 optical data, 3 bytes per channel, up to 3 channels per sample + public const byte HID_REPORT_ID_2 = 2; // LIS2DH accelerometer data, 2 bytes per channel, 3 channels per sample + public const byte HID_REPORT_ID_3 = 3; // reserved + private const byte USB_OVERFLOW = 8; + private const byte USB_WRITE_ERROR = 4; + private const byte USB_READ_ERROR = 2; + public const byte I2C_NACK_ERROR = 1; + private const byte API_FAIL = 0; //API functions return non-0 upon success, to easily denote 'true' for C programs + private const byte I2C_SUCCESS = 3; //we'll also use a non-0 value to denote success, since returning a '0' would lead to trouble when mixed with API's returns. + private const byte GP_SUCCESS = 3; + private const byte GP_FAIL = 0; + private const byte REPORT_SIZE = 64; + + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] before FlushQueue() + // mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + // mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + public static Mutex mutexGuardIOBuf = new Mutex(); + // TODO1: OS24EVK-57 2015-04-01 myHID.readHID2() needs to return result into IOBuf. But IOBuf = new byte[64], do we replace with IOBuf = new byte[128]? Does this affect readHID()? + public byte[] IOBuf = new byte[REPORT_SIZE]; + + private const int IOtimeout = 10000; + + // API declarations relating to device management (SetupDixxx and + // RegisterDeviceNotification functions). + + // from dbt.h + + internal const Int32 DBT_DEVNODES_CHANGED = 7; + internal const Int32 DBT_DEVICEARRIVAL = 0X8000; + internal const Int32 DBT_DEVICEREMOVECOMPLETE = 0X8004; + internal const Int32 DBT_DEVTYP_DEVICEINTERFACE = 5; + internal const Int32 DBT_DEVTYP_HANDLE = 6; + internal const Int32 DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4; + internal const Int32 DEVICE_NOTIFY_SERVICE_HANDLE = 1; + internal const Int32 DEVICE_NOTIFY_WINDOW_HANDLE = 0; + internal const Int32 WM_DEVICECHANGE = 0X219; + internal const Int32 SPDRP_HARDWAREID = 1; + + // from setupapi.h + + internal const Int32 DIGCF_PRESENT = 2; + internal const Int32 DIGCF_DEVICEINTERFACE = 0X10; + + // Two declarations for the DEV_BROADCAST_DEVICEINTERFACE structure. + + // Use this one in the call to RegisterDeviceNotification() and + // in checking dbch_devicetype in a DEV_BROADCAST_HDR structure: + + [StructLayout(LayoutKind.Sequential)] + internal class DEV_BROADCAST_DEVICEINTERFACE + { + internal Int32 dbcc_size; + internal Int32 dbcc_devicetype; + internal Int32 dbcc_reserved; + internal Guid dbcc_classguid; + internal Int16 dbcc_name; + } + + // Use this to read the dbcc_name String and classguid: + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal class DEV_BROADCAST_DEVICEINTERFACE_1 + { + internal Int32 dbcc_size; + internal Int32 dbcc_devicetype; + internal Int32 dbcc_reserved; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] + internal Byte[] dbcc_classguid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] + internal Char[] dbcc_name; + } + + [StructLayout(LayoutKind.Sequential)] + internal class DEV_BROADCAST_HDR + { + internal Int32 dbch_size; + internal Int32 dbch_devicetype; + internal Int32 dbch_reserved; + } + + internal struct SP_DEVICE_INTERFACE_DATA + { + internal Int32 cbSize; + internal System.Guid InterfaceClassGuid; + internal Int32 Flags; + internal IntPtr Reserved; + } + + //[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + //internal struct SP_DEVICE_INTERFACE_DETAIL_DATA + //{ + // internal Int32 cbSize; + // //internal String DevicePath; + // internal Char[] DevicePath; + //} + +// warning CS0649: Field 'Maxim.MAX30101.HID.SP_DEVINFO_DATA.cbSize' is never assigned to, and will always have its default value 0 +#pragma warning disable 0649 + internal struct SP_DEVINFO_DATA + { + internal Int32 cbSize; + internal System.Guid ClassGuid; + internal Int32 DevInst; + internal Int32 Reserved; + } +#pragma warning restore 0649 + + //HDEVINFO SetupDiGetClassDevs(const GUID *ClassGuid, PCTSTR Enumerator, HWND hwndParent, DWORD Flags); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags); + + //BOOL SetupDiDestroyDeviceInfoList(HDEVINFO DeviceInfoSet); + [DllImport("setupapi.dll", SetLastError = true)] + internal static extern Boolean SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); + + //BOOL SetupDiEnumDeviceInterfaces(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, const GUID *InterfaceClassGuid, DWORD MemberIndex, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + [DllImport("setupapi.dll", SetLastError = true)] + internal static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + [DllImport("setupapi.dll", SetLastError = true)] + // required to pass DeviceInfoData=null + internal static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData); + + //BOOL SetupDiGetDeviceInterfaceDetail(HDEVINFO DeviceInfoSet, PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, DWORD DeviceInterfaceDetailDataSize, PDWORD RequiredSize, PSP_DEVINFO_DATA DeviceInfoData); + //[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + //internal extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVINFO_DATA DeviceInfoData); + //[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + //// required to pass DeviceInfoData=null + //internal extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); + // cannot get SP_DEVICE_INTERFACE_DETAIL_DATA's DevicePath field to work properly, so use IntPtr instead of ref SP_DEVICE_INTERFACE_DATA + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + // required to pass DeviceInterfaceDetailData=null + internal static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, ref SP_DEVINFO_DATA DeviceInfoData); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + // required to pass DeviceInterfaceDetailData=null, DeviceInfoData=null + internal static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData); + + //BOOL SetupDiEnumDeviceInfo(HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); + [DllImport("setupapi.dll", SetLastError = true)] + internal static extern Boolean SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, Int32 MemberIndex, ref SP_DEVINFO_DATA DevInfoData); + + //BOOL SetupDiGetDeviceRegistryProperty(HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern Boolean SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DevInfoData, Int32 Property, IntPtr PropertyRegDataType, IntPtr PropertyBuffer, Int32 PropertyBufferSize, ref Int32 RequiredSize); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, Int32 Flags); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern Boolean UnregisterDeviceNotification(IntPtr Handle); + + // API declarations for HID communications. + + // from hidpi.h + // Typedef enum defines a set of integer constants for HidP_Report_Type + + internal const Int16 HidP_Input = 0; + internal const Int16 HidP_Output = 1; + internal const Int16 HidP_Feature = 2; + + [StructLayout(LayoutKind.Sequential)] + internal struct HIDD_ATTRIBUTES + { + internal Int32 Size; + internal UInt16 VendorID; + internal UInt16 ProductID; + internal UInt16 VersionNumber; + } + + [DllImport("hid.dll", SetLastError = true)] + internal static extern Boolean HidD_FlushQueue(SafeFileHandle HidDeviceObject); + + [DllImport("hid.dll", SetLastError = true)] + internal static extern Boolean HidD_GetAttributes(SafeFileHandle HidDeviceObject, ref HIDD_ATTRIBUTES Attributes); + + [DllImport("hid.dll", SetLastError = true)] + internal static extern void HidD_GetHidGuid(ref System.Guid HidGuid); + + public void getHidGuid(ref System.Guid hidGuid) + { + DebugMessage = string.Format("{0} entered", System.Reflection.MethodInfo.GetCurrentMethod().Name); +#if DEBUG + HidD_GetHidGuid(ref hidGuid); +#else + try + { + HidD_GetHidGuid(ref hidGuid); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } +#endif + DebugMessage = string.Format("{0} exited", System.Reflection.MethodInfo.GetCurrentMethod().Name); + } + + public string DebugMessage; + //public void TraceMessage(string message, + //[System.Runtime.CompilerServices.CallerMemberName] string memberName = "", + //[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "", + //[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0) + //{ + // Console.WriteLine("message: " + message); + // Console.WriteLine("member name: " + memberName); + // Console.WriteLine("source file path: " + sourceFilePath); + // Console.WriteLine("source line number: " + sourceLineNumber); + //} + + /// <summary> + /// FindDesiredHIDPathNamesFromGuid() only appends to desiredHIDPathNameArray. + /// Desired HIDs that have been removed from the system are removed from desiredHIDPathNameArray in openHIDhandles + /// </summary> + /// <param name="myGuid"></param> + private void FindDesiredHIDPathNamesFromGuid(System.Guid myGuid) + { + DebugMessage = string.Format("{0} entered", System.Reflection.MethodInfo.GetCurrentMethod().Name); + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message: Arithmetic operation resulted in an overflow. + // But I never see this kind of exception thrown from within the C# GUI. + // HID.findHIDs() still raises the exception in Matlab, + // Message: Arithmetic operation resulted in an overflow. + // + // % Required: connect to MAX30101EVKIT hardware + // fprintf('Connecting to MAX30101EVKIT hardware...\n'); + // for trial=0:2000 + // try + // %pause(1) % delay at least 1 second + // myMAX30101.myHID.findHIDs(); + // % Sometimes we get Error using MAX30101Example + // % If this happens, try clearing the workspace and run again. + // % Message: Arithmetic operation resulted in an overflow. + // % FindDesiredHIDPathNamesFromGuid + // % findHIDs + // % Source: MAX30101 + // % HelpLink: + // if (myMAX30101.myHID.isConnected()) + // break + // end + // catch me + // % disp(me) + // end + // end + // + // If matlab does successfully connect to USB, it is able to get + // streaming data through the PartialArrayIntAvailable event + // handler -- even though it can't understand + // System.Collections.ArrayList data, it does at least understand + // Array<System.Int32> or int[] data. + // + Int32 memberIndex = 0; + Int32 bufferSize = 0; + IntPtr deviceInfoSet = new System.IntPtr(); + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); + IntPtr deviceInterfaceDetailDataBuffer = IntPtr.Zero; + + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message: Arithmetic operation resulted in an overflow. + // diagnostic: trying to avoid "Arithmetic overflow" from matlab. Limit the number of HID devices to be checked. + const int memberIndexLimit = 100; + +#if DEBUG +#else + try + { +#endif + DebugMessage = string.Format("{0} first deviceInfoSet = SetupDiGetClassDevs(ref myGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);", System.Reflection.MethodInfo.GetCurrentMethod().Name); + deviceInfoSet = SetupDiGetClassDevs(ref myGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + memberIndex = 0; + + // The cbSize element of the DeviceInterfaceData structure must be set to the structure's size in bytes. + // The size is 28 bytes for 32-bit code and 32 bits for 64-bit code. + DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData); + + while (memberIndex < memberIndexLimit) + { + // Begin with memberIndex = 0 and increment through the device information set until no more devices are available. + DebugMessage = string.Format("{0} memberIndex={1} first SetupDiEnumDeviceInterfaces ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + if (!SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref myGuid, memberIndex, ref DeviceInterfaceData)) + { + break; + } + + DebugMessage = string.Format("{0} memberIndex={1} first SetupDiGetDeviceInterfaceDetail ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, IntPtr.Zero); + //tempLastError = GetLastError(); + //if (tempLastError != ERROR_INSUFFICIENT_BUFFER) // ERROR_INSUFFICIENT_BUFFER is expected on this first call + // break; + //FIXME add error check + + // Allocate memory for the SP_DEVICE_INTERFACE_DETAIL_DATA structure using the returned buffer size. + DebugMessage = string.Format("{0} memberIndex={1} Marshal.AllocHGlobal(bufferSize) ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + deviceInterfaceDetailDataBuffer = Marshal.AllocHGlobal(bufferSize); + // Returns a System.IntPtr pointer to the newly allocated global heap memory. + // This memory must be released using the Marshal.FreeHGlobal method. + // Marshal.AllocHGlobal(numBytes) could throw OutOfMemoryException ? + + // Store cbSize in the first bytes of the array. The number of bytes varies with 32- and 64-bit systems. + DebugMessage = string.Format("{0} memberIndex={1} Marshal.WriteInt32 ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + Marshal.WriteInt32(deviceInterfaceDetailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); + + // Call SetupDiGetDeviceInterfaceDetail again. + // This time, pass a pointer to DetailDataBuffer and the returned required buffer size. + DebugMessage = string.Format("{0} memberIndex={1} second SetupDiGetDeviceInterfaceDetail ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, deviceInterfaceDetailDataBuffer, bufferSize, ref bufferSize, IntPtr.Zero)) + break; + + // Skip over cbsize (4 bytes) to get the address of the devicePathName. + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message + DebugMessage = string.Format("{0} memberIndex={1} IntPtr pDevicePathName = IntPtr.Add(deviceInterfaceDetailDataBuffer, 4); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + IntPtr pDevicePathName = IntPtr.Add(deviceInterfaceDetailDataBuffer, 4); + // DebugMessage = string.Format("{0} memberIndex={1} new IntPtr(deviceInterfaceDetailDataBuffer.ToInt32() + 4); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + // BAD CODE. IntPtr pDevicePathName = new IntPtr(deviceInterfaceDetailDataBuffer.ToInt32() + 4); + // BAD CODE. assumes the pointer is a 32-bit address, intermittently fails from 64-bit matlab client. + + // Get the String containing the devicePathName. + DebugMessage = string.Format("{0} memberIndex={1} Marshal.PtrToStringAuto(pDevicePathName); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + string tempPathName = Marshal.PtrToStringAuto(pDevicePathName); + + // match any device pathname that contains deviceID (case-insensitive match) "Vid_{0:x4}&Pid_{1:x4}" + DebugMessage = string.Format("{0} memberIndex={1} if (tempPathName.ToLower().IndexOf(deviceID.ToLower()) != -1) ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + if (tempPathName.ToLower().IndexOf(deviceID.ToLower()) != -1) + { + DebugMessage = string.Format("{0} memberIndex={1} desiredHIDPathNameArray.Add(tempPathName); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + desiredHIDPathNameArray.Add(tempPathName); + } + + DebugMessage = string.Format("{0} memberIndex={1} memberIndex = memberIndex + 1; ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message + // why didn't they call Marshal.FreeHGlobal here, inside the while loop? Isn't this a memory leak? + // Free the memory allocated previously by AllocHGlobal. + if (deviceInterfaceDetailDataBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(deviceInterfaceDetailDataBuffer); + deviceInterfaceDetailDataBuffer = IntPtr.Zero; + } + + memberIndex = memberIndex + 1; + } +#if DEBUG + // Free the memory allocated previously by AllocHGlobal. + if (deviceInterfaceDetailDataBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(deviceInterfaceDetailDataBuffer); + + if (deviceInfoSet != IntPtr.Zero) + SetupDiDestroyDeviceInfoList(deviceInfoSet); +#else + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + finally + { + // Free the memory allocated previously by AllocHGlobal. + if (deviceInterfaceDetailDataBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(deviceInterfaceDetailDataBuffer); + + if (deviceInfoSet != IntPtr.Zero) + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } +#endif + DebugMessage = string.Format("{0} exited", System.Reflection.MethodInfo.GetCurrentMethod().Name); + } + + private void FindAllHIDPathNamesFromGuid(System.Guid myGuid, ArrayList allHIDPathNameArray) + { + DebugMessage = string.Format("{0} entered", System.Reflection.MethodInfo.GetCurrentMethod().Name); + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message: Arithmetic operation resulted in an overflow. + // But I never see this kind of exception thrown from within the C# GUI. + // HID.findHIDs() still raises the exception in Matlab, + // Message: Arithmetic operation resulted in an overflow. + Int32 memberIndex = 0; + Int32 bufferSize = 0; + IntPtr deviceInfoSet = new System.IntPtr(); + SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA(); + IntPtr deviceInterfaceDetailDataBuffer = IntPtr.Zero; + + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message: Arithmetic operation resulted in an overflow. + // diagnostic: trying to avoid "Arithmetic overflow" from matlab. Limit the number of HID devices to be checked. + const int memberIndexLimit = 100; + + +#if DEBUG +#else + try + { +#endif + DebugMessage = string.Format("{0} first deviceInfoSet = SetupDiGetClassDevs(ref myGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);", System.Reflection.MethodInfo.GetCurrentMethod().Name); + deviceInfoSet = SetupDiGetClassDevs(ref myGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + memberIndex = 0; + + // The cbSize element of the DeviceInterfaceData structure must be set to the structure's size in bytes. + // The size is 28 bytes for 32-bit code and 32 bits for 64-bit code. + DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData); + + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message: Arithmetic operation resulted in an overflow. + while (memberIndex < memberIndexLimit) + { + DebugMessage = string.Format("{0} memberIndex={1} first SetupDiEnumDeviceInterfaces ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + if (!SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref myGuid, memberIndex, ref DeviceInterfaceData)) + { + break; + } + + DebugMessage = string.Format("{0} memberIndex={1} first SetupDiGetDeviceInterfaceDetail ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, IntPtr.Zero); + + // Allocate memory for the SP_DEVICE_INTERFACE_DETAIL_DATA structure using the returned buffer size. + DebugMessage = string.Format("{0} memberIndex={1} Marshal.AllocHGlobal(bufferSize) ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + deviceInterfaceDetailDataBuffer = Marshal.AllocHGlobal(bufferSize); + + // Store cbSize in the first bytes of the array. The number of bytes varies with 32- and 64-bit systems. + DebugMessage = string.Format("{0} memberIndex={1} Marshal.WriteInt32 ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + Marshal.WriteInt32(deviceInterfaceDetailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); + + // Call SetupDiGetDeviceInterfaceDetail again. + // This time, pass a pointer to deviceInterfaceDetailDataBuffer and the returned required buffer size. + DebugMessage = string.Format("{0} memberIndex={1} second SetupDiGetDeviceInterfaceDetail ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, deviceInterfaceDetailDataBuffer, bufferSize, ref bufferSize, IntPtr.Zero)) + break; + + // Skip over cbsize (4 bytes) to get the address of the devicePathName. + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message + DebugMessage = string.Format("{0} memberIndex={1} IntPtr pDevicePathName = IntPtr.Add(deviceInterfaceDetailDataBuffer, 4); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + // example: IntPtr right way to add an offset (portable to 64-bit clients) + IntPtr pDevicePathName = IntPtr.Add(deviceInterfaceDetailDataBuffer, 4); + // DebugMessage = string.Format("{0} memberIndex={1} IntPtr pDevicePathName = new IntPtr(deviceInterfaceDetailDataBuffer.ToInt32() + 4); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + // BAD CODE. IntPtr pDevicePathName = new IntPtr(deviceInterfaceDetailDataBuffer.ToInt32() + 4); + // BAD CODE. assumes the pointer is a 32-bit address, intermittently fails from 64-bit matlab client. + + // Get the String containing the devicePathName. + DebugMessage = string.Format("{0} memberIndex={1} Marshal.PtrToStringAuto(pDevicePathName); ", System.Reflection.MethodInfo.GetCurrentMethod().Name, memberIndex); + allHIDPathNameArray.Add(Marshal.PtrToStringAuto(pDevicePathName)); + + // VERIFY: https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message + // why didn't they call Marshal.FreeHGlobal here, inside the while loop? Isn't this a memory leak? + // Free the memory allocated previously by AllocHGlobal. + if (deviceInterfaceDetailDataBuffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(deviceInterfaceDetailDataBuffer); + deviceInterfaceDetailDataBuffer = IntPtr.Zero; + } + + memberIndex = memberIndex + 1; + } +#if DEBUG + // Free the memory allocated previously by AllocHGlobal. + if (deviceInterfaceDetailDataBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(deviceInterfaceDetailDataBuffer); + + if (deviceInfoSet != IntPtr.Zero) + SetupDiDestroyDeviceInfoList(deviceInfoSet); +#else + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + finally + { + // Free the memory allocated previously by AllocHGlobal. + if (deviceInterfaceDetailDataBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(deviceInterfaceDetailDataBuffer); + + if (deviceInfoSet != IntPtr.Zero) + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } +#endif + DebugMessage = string.Format("{0} exited", System.Reflection.MethodInfo.GetCurrentMethod().Name); + } + + public bool isConnected() + { + try + { + return desiredHIDPathNameArray.Count != 0 ? true : false; + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + + /// <summary> + /// Called when a WM_DEVICECHANGE message has arrived, + /// indicating that a device has been attached or removed. + /// + /// </summary> + /// <param name="m"> a message with information about the device </param> + /// <returns>true on HID device arrival or remove complete</returns> + public bool HandleWMDeviceChangeMessage(Message m) + { + // Example code: + // + // <code> + // protected override void WndProc(ref Message m) + // { + // if (myHID != null) + // { + // if (myHID.HandleWMDeviceChangeMessage(m) /* m.Msg == HID.WM_DEVICECHANGE */ ) + // { + // // optional: handle newly arrived connection or surprise disconnect + // } + // } + // // Let the base form process the message. + // base.WndProc(ref m); + // } + // </code> + // + // https://jira.maxim-ic.com/browse/OS24EVK-59 WndProc if HID.WM_DEVICECHANGE do HID.OnDeviceChange(Message m) + try + { + if (m.Msg == HID.WM_DEVICECHANGE) + { + //if ((int)m.WParam == HID.DBT_DEVNODES_CHANGED) //this always occurs when any USB device is attached/detached. Use this if not registering for device notifications. + if ( + ( m.WParam.ToInt32() == HID.DBT_DEVICEARRIVAL + || m.WParam.ToInt32() == HID.DBT_DEVICEREMOVECOMPLETE + ) + && + (m.LParam.ToInt32() != 0) + && + DeviceIDMatch(m) + ) + { + closeHIDhandles(); + findHIDs(); + // cboEVB_init(); + return true; + } + } + return false; + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + /// <summary> + /// findHIDs() called upon startup or if a desired HID has been inserted or removed + /// </summary> + public void findHIDs() + { + DebugMessage = string.Format("{0} entered", System.Reflection.MethodInfo.GetCurrentMethod().Name); + ArrayList allHIDPathNameArray = new ArrayList(); + System.Guid hidGuid = new System.Guid(); + + // https://jira.maxim-ic.com/browse/OS24EVK-59 (intermittent) Matlab Exception Message: Arithmetic operation resulted in an overflow. +#if DEBUG +#else + try + { +#endif + HidD_GetHidGuid(ref hidGuid); + // FindDesiredHIDPathNamesFromGuid() builds desiredHIDPathNameArray which is a list of all desired HIDs in the system + // desiredHIDPathNameArray is a global list and is used to maintain the order of desired HIDs in the selection list. + // USB ports have different priorities, and the user could have attached the first desired HID to a lower priority port. + // Hence, when another desired HID is attached to a higher priority port, it will come earlier in allHIDPathNameArray, but it will be maintained in the same attachment order in desiredHIDPathNameArray. + // Note that desiredHIDPathNameArray is only appended to or deleted from; it is never recreated in whole. + // FIXME desiredHIDPathNameArray will get duplicate pathnames for desired HIDs that were already attached, but these will be removed by openHIDhandles() since they're in allHIDPathNameArray only once. + // These duplicates pathnames are appended after the initial list, so they won't affect order. + FindDesiredHIDPathNamesFromGuid(hidGuid); + // FindAllHIDPathNamesFromGuid() builds allHIDPathNameArray which is a list of all HIDs in the system. It is recreated every time a desired HID is attached or removed. + FindAllHIDPathNamesFromGuid(hidGuid, allHIDPathNameArray); + // openHIDhandles() gets handles for all desired HIDs + // openHIDhandles() loops through all attached HIDs and checks for a match of each item in desiredHIDPathNameArray. This maintains the attachement order. + // If a previously attached HID has been removed, it won't be found in allHIDPathNameArray and it will be removed from desiredHIDPathNameArray. + openHIDhandles(allHIDPathNameArray); + if (desiredHIDPathNameArray.Count != 0) + { + prepareForOverlappedTransfer(); + } +#if DEBUG +#else + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } +#endif + DebugMessage = string.Format("{0} exited", System.Reflection.MethodInfo.GetCurrentMethod().Name); + } + + private void openHIDhandles(ArrayList allHIDPathNameArray) + { + DebugMessage = string.Format("{0} entered", System.Reflection.MethodInfo.GetCurrentMethod().Name); + int desiredHIDPathNameArrayCounter; + int allHIDPathNameArrayCounter; + bool found_installed_device; + try + { + desiredHIDPathNameArrayCounter = 0; + while (desiredHIDPathNameArrayCounter < desiredHIDPathNameArray.Count) // count will change if a previously installed device has been removed, so don't use a for loop + { + found_installed_device = false; + allHIDPathNameArrayCounter = 0; + while (allHIDPathNameArrayCounter < allHIDPathNameArray.Count) + { + if ((string)allHIDPathNameArray[allHIDPathNameArrayCounter] == (string)desiredHIDPathNameArray[desiredHIDPathNameArrayCounter]) + { + writeHandle = FileIO.CreateFile((string)desiredHIDPathNameArray[desiredHIDPathNameArrayCounter], FileIO.GENERIC_READ | FileIO.GENERIC_WRITE, FileIO.FILE_SHARE_READ | FileIO.FILE_SHARE_WRITE, IntPtr.Zero, FileIO.OPEN_EXISTING, 0, 0); + if (!(writeHandle.IsInvalid)) + { + readHandle = FileIO.CreateFile((string)desiredHIDPathNameArray[desiredHIDPathNameArrayCounter], FileIO.GENERIC_READ | FileIO.GENERIC_WRITE, FileIO.FILE_SHARE_READ | FileIO.FILE_SHARE_WRITE, IntPtr.Zero, FileIO.OPEN_EXISTING, FileIO.FILE_FLAG_OVERLAPPED, 0); + if (!(readHandle.IsInvalid)) + { + writeHandleArray.Add(writeHandle); + readHandleArray.Add(readHandle); + allHIDPathNameArray.RemoveAt(allHIDPathNameArrayCounter); // remove it so we don't repeatedly add it when we check for new devices (after we finish checking for previously installed devices) + found_installed_device = true; + } + else + { + writeHandle.Close(); + writeHandle = null; + readHandle = null; + } + } + else + { + writeHandle = null; + readHandle = null; + } + break; + } + allHIDPathNameArrayCounter++; + } + if (found_installed_device == false) // no match in all allHIDPathNameArray elements; the device has been removed so remove its pathname from desiredHIDPathNameArray. Don't use counter at max count to check if not found, since max count can change. + desiredHIDPathNameArray.RemoveAt(desiredHIDPathNameArrayCounter); // decrements count by 1; don't increment desiredHIDPathNameArrayCounter + else + desiredHIDPathNameArrayCounter++; + } + } + catch (Exception ex) + { + DebugMessage = string.Format("{0} exception {1}", System.Reflection.MethodInfo.GetCurrentMethod().Name, ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + DebugMessage = string.Format("{0} exited", System.Reflection.MethodInfo.GetCurrentMethod().Name); + } + + public void closeHIDhandles() + { + try + { + if (desiredHIDPathNameArray.Count != 0) + { + foreach (Object obj in writeHandleArray) + ((SafeFileHandle)obj).Close(); + writeHandleArray.Clear(); + writeHandle = null; + foreach (Object obj in readHandleArray) + ((SafeFileHandle)obj).Close(); + readHandleArray.Clear(); + readHandle = null; + } + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + private void prepareForOverlappedTransfer() + { + try + { + EventObject = FileIO.CreateEvent(IntPtr.Zero, false, false, String.Empty); + managedOverlapped.OffsetLow = 0; + managedOverlapped.OffsetHigh = 0; + managedOverlapped.EventHandle = EventObject; // HIDOverlapped is the overlapped structure used in ReadFile; EventObject will be signaled upon completion of ReadFile + nonManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(managedOverlapped)); + Marshal.StructureToPtr(managedOverlapped, nonManagedOverlapped, false); + + nonManagedBuffer = Marshal.AllocHGlobal(REPORT_SIZE); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void FlushQueue() + { + try + { + if (writeHandle == null) + { + return; + } + if (readHandle == null) + { + return; + } + + HidD_FlushQueue(writeHandle); + HidD_FlushQueue(readHandle); + } + catch + { + throw new Exception(new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void writeReadHID() + { + try + { + if (writeHandle == null) + { + return; + } + if (readHandle == null) + { + return; + } + + writeHID(); + readHID(); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void writeHID() + { + if (writeHandle == null) + { + return; + } + if (readHandle == null) + { + return; + } + + int BytesSucceed; + bool api_status; + + try + { + BytesSucceed = 0; + Marshal.Copy(IOBuf, 0, nonManagedBuffer, REPORT_SIZE); + api_status = FileIO.WriteFile(writeHandle, nonManagedBuffer, REPORT_SIZE, ref BytesSucceed, IntPtr.Zero); + //endpoint in interrupt at uC occurs after this WriteFile call since the in data is ready and the host takes it; endpoint in interrupt does not occur after ReadFile call + + if (api_status == false) + { + //MessageBox.Show(Err.LastDllError); + throw new Exception(support.ResultOfAPICall("API WriteFile error")); + } + } + catch (Exception ex) + { + throw new Exception(ex.Message + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void readHID() + { + if (writeHandle == null) + { + return; + } + if (readHandle == null) + { + return; + } + + int BytesSucceed; + bool api_status; + int status; + + try + { + Array.Clear(IOBuf, 0, IOBuf.Length); + + BytesSucceed = 0; + + api_status = FileIO.ReadFile(readHandle, nonManagedBuffer, REPORT_SIZE, ref BytesSucceed, nonManagedOverlapped); + + if (api_status == false) + { + //MsgBox(Err.LastDllError) + status = FileIO.WaitForSingleObject(EventObject, IOtimeout); + + if (status != FileIO.WAIT_OBJECT_0) + { + api_status = FileIO.CancelIo(readHandle); + throw new Exception(support.ResultOfAPICall("API ReadFile error")); + } + FileIO.GetOverlappedResult(readHandle, nonManagedOverlapped, ref BytesSucceed, false); + } + + // TODO1: OS24EVK-57 2015-04-01 myHID.readHID2() needs to return result into IOBuf. But IOBuf = new byte[64], do we replace with IOBuf = new byte[128]? Does this affect readHID()? + if (BytesSucceed > IOBuf.Length) + { + IOBuf = new byte[BytesSucceed]; + Array.Clear(IOBuf, 0, IOBuf.Length); + } + Marshal.Copy(nonManagedBuffer, IOBuf, 0, BytesSucceed); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void readHID2() // this function is for testing multiple report read on a single ReadFile() + { + if (writeHandle == null) + { + return; + } + if (readHandle == null) + { + return; + } + + int BytesSucceed; + bool api_status; + int status; + + try + { + int size = 128; + //byte[] buf = new byte[size]; + IntPtr nonManagedBuf = Marshal.AllocHGlobal(size); + //Array.Clear(buf, 0, size); + + BytesSucceed = 0; + + api_status = FileIO.ReadFile(readHandle, nonManagedBuf, size, ref BytesSucceed, nonManagedOverlapped); + + if (api_status == false) + { + //MsgBox(Err.LastDllError) + status = FileIO.WaitForSingleObject(EventObject, IOtimeout); + + if (status != FileIO.WAIT_OBJECT_0) + { + api_status = FileIO.CancelIo(readHandle); + throw new Exception(support.ResultOfAPICall("API ReadFile error")); + } + FileIO.GetOverlappedResult(readHandle, nonManagedOverlapped, ref BytesSucceed, false); + } + + // TODO1: OS24EVK-57 2015-04-01 myHID.readHID2() needs to return result into IOBuf. But IOBuf = new byte[64], do we replace with IOBuf = new byte[128]? Does this affect readHID()? + if (BytesSucceed > IOBuf.Length) + { + IOBuf = new byte[BytesSucceed]; + Array.Clear(IOBuf, 0, IOBuf.Length); + } + Marshal.Copy(nonManagedBuf, IOBuf, 0, BytesSucceed); + + if (nonManagedBuf != IntPtr.Zero) + Marshal.FreeHGlobal(nonManagedBuffer); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + public void freeHeap() + { + try + { + if (nonManagedBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(nonManagedBuffer); + if (nonManagedOverlapped != IntPtr.Zero) + Marshal.FreeHGlobal(nonManagedOverlapped); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + /// <summary> + /// Requests to receive a notification when a device is attached or removed. + /// </summary> + /// + /// <param name="formHandle"> handle to the window that will receive device events. </param> + /// <param name="classGuid"> device interface GUID. </param> + /// <param name="deviceNotificationHandle"> returned device notification handle. </param> + /// + /// <returns> + /// True on success. + /// </returns> + /// + public Boolean RegisterForDeviceNotifications(IntPtr formHandle, Guid classGuid, ref IntPtr deviceNotificationHandle) + { + DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE(); + IntPtr devBroadcastDeviceInterfaceBuffer = IntPtr.Zero; + Int32 size = 0; + + try + { + size = Marshal.SizeOf(devBroadcastDeviceInterface); + devBroadcastDeviceInterface.dbcc_size = size; + devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + devBroadcastDeviceInterface.dbcc_reserved = 0; + devBroadcastDeviceInterface.dbcc_classguid = classGuid; + + // Allocate memory for the buffer that holds the DEV_BROADCAST_DEVICEINTERFACE structure. + devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(size); + + // Copy the DEV_BROADCAST_DEVICEINTERFACE structure to the buffer. + // Set fDeleteOld True to prevent memory leaks. + Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true); + + // *** + // API function + + // summary + // Request to receive notification messages when a device in an interface class + // is attached or removed. + + // parameters + // Handle to the window that will receive device events. + // Pointer to a DEV_BROADCAST_DEVICEINTERFACE to specify the type of + // device to send notifications for. + // DEVICE_NOTIFY_WINDOW_HANDLE indicates the handle is a window handle. + + // Returns + // Device notification handle or NULL on failure. + // *** + + deviceNotificationHandle = RegisterDeviceNotification(formHandle, devBroadcastDeviceInterfaceBuffer, DEVICE_NOTIFY_WINDOW_HANDLE); + + // Marshal data from the unmanaged block devBroadcastDeviceInterfaceBuffer to the managed object devBroadcastDeviceInterface + // why? + Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface); + + return deviceNotificationHandle.ToInt32() == IntPtr.Zero.ToInt32() ? false : true; + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + finally + { + // Free the memory allocated previously by AllocHGlobal. + if (devBroadcastDeviceInterfaceBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer); + } + } + + /// <summary> + /// Requests to stop receiving notification messages when a device in an + /// interface class is attached or removed. + /// </summary> + /// + /// <param name="deviceNotificationHandle"> handle returned previously by + /// RegisterDeviceNotification. </param> + + public void StopReceivingDeviceNotifications(IntPtr deviceNotificationHandle) + { + try + { + UnregisterDeviceNotification(deviceNotificationHandle); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public Boolean DeviceIDMatch(Message m) + { + Int32 stringSize; + + try + { + DEV_BROADCAST_HDR devBroadcastHeader = new DEV_BROADCAST_HDR(); + DEV_BROADCAST_DEVICEINTERFACE_1 devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE_1(); + + // The LParam parameter of Message is a pointer to a DEV_BROADCAST_HDR structure. + Marshal.PtrToStructure(m.LParam, devBroadcastHeader); + if ((devBroadcastHeader.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)) + { + // The dbch_devicetype parameter indicates that the event applies to a device interface. + // So the structure in LParam is actually a DEV_BROADCAST_INTERFACE structure, + // which begins with a DEV_BROADCAST_HDR. + + // Obtain the number of characters in dbch_name by subtracting the 32 bytes + // in the strucutre that are not part of dbch_name and dividing by 2 because there are + // 2 bytes per character. + + stringSize = System.Convert.ToInt32((devBroadcastHeader.dbch_size - 32) / 2); + + // The dbcc_name parameter of devBroadcastDeviceInterface contains the device name. + // Trim dbcc_name to match the size of the String. + + devBroadcastDeviceInterface.dbcc_name = new Char[stringSize + 1]; + + // Marshal data from the unmanaged block pointed to by m.LParam + // to the managed object devBroadcastDeviceInterface. + + Marshal.PtrToStructure(m.LParam, devBroadcastDeviceInterface); + + // Store the device name in a String. + + String DeviceNameString = new String(devBroadcastDeviceInterface.dbcc_name, 0, stringSize); + + // Compare the name of the newly attached device with the name of the device + // the application is accessing (deviceID). + // Set ignorecase True. + + return (DeviceNameString.ToLower().IndexOf(deviceID.ToLower()) == -1) ? false : true; + } + else + return false; + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void reportID() + { + try + { + if (explicit_report_id) + IOBuf[0] = SHORT_REPORT_ID; // explicit out report ID in HID's descriptor + else + IOBuf[0] = DEFAULT_REPORT_ID; // default report ID; this byte is dropped in transfer, so we don't really waste a byte transmitting it + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public int readI2C(byte deviceAddress, byte numDataBytes, byte numRegBytes, byte[] readData, byte[] reg, bool ignoreNACK = false) + { + //readData is passed back to caller with the read data + int status; + int i; + + try + { + if (numDataBytes > REPORT_SIZE - 2) { + throw new Exception("USB buffer overflow"); + } + + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in readI2C + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + + reportID(); + IOBuf[1] = 6; // I2C transaction -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = (byte)(deviceAddress | 1); // set read bit + IOBuf[3] = numDataBytes; + IOBuf[4] = numRegBytes; + // TODO1: OS24EVK-57 HID readI2C no validation that numRegBytes == reg.Length ? + for (i = 0; i < numRegBytes; i++) { + IOBuf[i + 5] = reg[i]; + } + + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + + status = checkNACK(); + + // checkNACK also reads the data, assuming NACK didn't occur + if (status == I2C_NACK_ERROR && ignoreNACK == false) { + throw new Exception("invalid I2C address"); + } + + for (i = 0; i < numDataBytes; i++) { + readData[i] = IOBuf[i + 2]; + } + + return status; // the caller will not need the return value if an exception is thrown + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + public void writeI2C(byte deviceAddress, byte numDataBytes, byte numRegBytes, byte[] data, byte[] reg, bool ignoreNACK = false) + { + int i; + + try + { + if (numDataBytes > REPORT_SIZE - 5) { + throw new Exception("USB buffer overflow"); + } + + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in writeI2C + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + + //send data to tell uC to do I2C read from slave + reportID(); + IOBuf[1] = 6; // I2C transaction -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = deviceAddress; + IOBuf[3] = numDataBytes; + IOBuf[4] = numRegBytes; + for (i = 0; i < numRegBytes; i++) { + IOBuf[i + 5] = reg[i]; + } + for (i = 0; i < numDataBytes; i++) { + IOBuf[i + 5 + numRegBytes] = data[i]; + } + + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + + if (checkNACK() == I2C_NACK_ERROR && ignoreNACK == false) { + throw new Exception("invalid I2C address"); + } + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + private int checkNACK() + { + // Check if I2C write was really succesful or a NACK occurred, since this is independent of USB success + // This function also reads all the data from the report. If NACK occured, the data is invalid. + try + { + readHID(); + return (IOBuf[1] == 0 ? I2C_SUCCESS : I2C_NACK_ERROR); // the caller will not need the return value if an exception is thrown + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + // only allows 2 byte data + public void setBitI2C(byte deviceAddress, int reg, byte range, int data, bool ignoreNACK = false) + { + int i; + + try + { + int LSb = range & 0xF; + int MSb = (range & 0xF0) >> 4; + if (MSb < LSb) + throw new Exception("invalid bit range"); + //if (numDataBytes > REPORT_SIZE - 5) + // throw new Exception("USB buffer overflow"); + + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in setBitI2C + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + + Array.Clear(IOBuf, 0, IOBuf.Length); + + //send data to tell uC to do I2C read from slave + reportID(); + IOBuf[1] = 6; // I2C transaction -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = deviceAddress; + byte numDataBytes = (byte)(MSb > 7 ? 2 : 1); + IOBuf[3] = numDataBytes; + byte numRegBytes = (byte)Math.Ceiling(reg / 255.0); + IOBuf[4] = numRegBytes; + for (i = 0; i < numRegBytes; i++) + IOBuf[i + 5] = (byte)((reg>>(8*i)) & 0xFF); + //for (i = 0; i < numDataBytes; i++) + // IOBuf[i + 5 + numRegBytes] = data[i]; + + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + + if (checkNACK() == I2C_NACK_ERROR && ignoreNACK == false) + throw new Exception("invalid I2C address"); + } + catch (Exception ex) + { + throw new Exception(ex.Message + Environment.NewLine + new System.Diagnostics.StackFrame().GetMethod().Name); + } + } + + /// <summary> + /// <para>Firmware command code 0 IN = Firmware version</para> + /// + /// </summary> + /// <param name="VerMajor"></param> + /// <param name="VerMinor"></param> + /// <param name="verYearHundreds"></param> + /// <param name="verYear"></param> + /// <param name="verMonth"></param> + /// <param name="verDay"></param> + public void FirmwareVersion(out byte VerMajor, out byte VerMinor, out byte verYearHundreds, out byte verYear, out byte verMonth, out byte verDay) + { + if (FirmwareINT0Enabled != 0) + { + // firmware is busy streaming out HID reports, + // so return a previously retrieved firmware version. + VerMajor = _VerMajor; + VerMinor = _VerMinor; + verYearHundreds = _verYearHundreds; + verYear = _verYear; + verMonth = _verMonth; + verDay = _verDay; + return; + } + // https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in FirmwareVersion + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 0; // Firmware version -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + writeReadHID(); + VerMajor = IOBuf[2]; + VerMinor = IOBuf[3]; + verYearHundreds = IOBuf[4]; + verYear = IOBuf[5]; + verMonth = IOBuf[6]; + verDay = IOBuf[7]; + // + // save the recently read values + _VerMajor = VerMajor; + _VerMinor = VerMinor; + _verYearHundreds = verYearHundreds; + _verYear = verYear; + _verMonth = verMonth; + _verDay = verDay; + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + private byte _VerMajor = 0; + private byte _VerMinor = 0; + private byte _verYearHundreds = 0; + private byte _verYear = 0; + private byte _verMonth = 0; + private byte _verDay = 0; + + /// <summary> + /// <para>Firmware command code 1 OUT = LED (C51F321 P2.2=red, P2.1=green) </para> + /// + /// </summary> + /// <param name="xxxxxxP22P21">0x02=P2.2 Red, 0x01=P2.1 Green</param> + public void LEDSet(byte xxxxxxP22P21) + { + // https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers LEDSet(byte xxxxxxP22P21) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in LEDSet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 1; // case (1): //LED (C51F321 P2.2=red, P2.1=green) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 0; // case (0): // write + IOBuf[3] = xxxxxxP22P21; + // Led2 = IO_BUFFER.Ptr[2+gOffset] & 1; // sbit Led2 = P2^1; + // Led1 = (IO_BUFFER.Ptr[2+gOffset] & 2) >> 1; // sbit Led1 = P2^2; + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 1 IN = GPIOP0Get read I/O pins P0.6, P0.7</para> + /// + /// </summary> + /// <param name="xxxxxxP06P07"></param> + public void GPIOP0Get(out byte xxxxxxP06P07) + { + // todo: verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers GPIOP0Get(out byte xxxxxxP06P07) + // https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: Rename LEDGet(out byte xxxxxxP06P07) to GPIOP0Get(out byte xxxxxxP06P07) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in GPIOP0Get + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 1; // case (1): //LED (C51F321 P2.2=red, P2.1=green) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 1; // case (1): // read + writeReadHID(); + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0 + gOffset] = 0; // error status + // IO_BUFFER.Ptr[1 + gOffset] = ((P0 & 0x80) >> 7) + ((P0 & 0x40) >> 5); + xxxxxxP06P07 = IOBuf[2]; + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 2 OUT = GPIO configuration (C51F321 P1 and P2) </para> + /// + /// </summary> + /// <param name="P1MDOUT"></param> + /// <param name="P2MDOUT"></param> + /// <param name="weakPullupDisable"></param> + public void GPIOP1P2ConfigSet(byte P1MDOUT, byte P2MDOUT, byte weakPullupDisable) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers GPIOP1P2ConfigSet(byte P1MDOUT, byte P2MDOUT, byte weakPullupDisable) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in GPIOP1P2ConfigSet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 2; // case (2): //GPIO configuration (C51F321 P1 and P2) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 0; // case (0): // write + IOBuf[3] = P1MDOUT; // P1MDOUT = IO_BUFFER.Ptr[2+gOffset]; // P1 push-pull (1) or open-collector (0) + IOBuf[4] = P2MDOUT; // P2MDOUT = IO_BUFFER.Ptr[3 + gOffset]; // P2 push-pull (1) or open-collector (0) + IOBuf[5] = weakPullupDisable; // (IO_BUFFER.Ptr[4 + gOffset] & 1) ? (XBR1 |= 0x80) : (XBR1 &= 0x7F); // weak pull up disable (open collector only); 1 disabled, 0 enabled + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 2 IN = GPIO configuration (C51F321 P1 and P2) </para> + /// + /// </summary> + /// <param name="P1MDOUT"></param> + /// <param name="P2MDOUT"></param> + /// <param name="weakPullupDisable"></param> + public void GPIOP1P2ConfigGet(out byte P1MDOUT, out byte P2MDOUT, out byte weakPullupDisable) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers GPIOP1P2ConfigGet(out byte P1MDOUT, out byte P2MDOUT, out byte weakPullupDisable) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in GPIOP1P2ConfigGet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 2; // case (2): //GPIO configuration (C51F321 P1 and P2) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 1; // case (1): // read + writeReadHID(); + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0 + gOffset] = 0; // error status + // IO_BUFFER.Ptr[1 + gOffset] = P1MDOUT; // P1 push-pull (1) or open-collector (0) + // IO_BUFFER.Ptr[2 + gOffset] = P2MDOUT; // P2 push-pull (1) or open-collector (0) + // IO_BUFFER.Ptr[3 + gOffset] = (XBR1 & 0x80) >> 7; // weakPullupDisable + P1MDOUT = IOBuf[2]; + P2MDOUT = IOBuf[3]; + weakPullupDisable = IOBuf[4]; + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 3 OUT = GPIO value (C51F321 P1 and P2) </para> + /// + /// </summary> + /// <param name="P1"></param> + /// <param name="P2"></param> + public void GPIOP1P2Out(byte P1, byte P2) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers GPIOP1P2Out(byte P1, byte P2) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in GPIOP1P2Out + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 3; // case (3): //GPIO value (C51F321 P1 and P2) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 0; // case (0): // write + IOBuf[3] = P1; // P1 = IO_BUFFER.Ptr[2 + gOffset]; // P1 HI (1) or LO (0); set P1==1 and P1MDOUT==1 for HI-Z + IOBuf[4] = P2; // P2 = IO_BUFFER.Ptr[3 + gOffset]; // P2 HI (1) or LO (0); set P2==1 and P1MDOUT==2 for HI-Z + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 3 IN = GPIO value (C51F321 P1 and P2) </para> + /// + /// </summary> + /// <param name="P1"></param> + /// <param name="P2"></param> + public void GPIOP1P2In(out byte P1, out byte P2) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers GPIOP1P2In(out byte P1, out byte P2) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in GPIOP1P2In + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 3; // case (3): //GPIO value (C51F321 P1 and P2) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 1; // case (1): // read + writeReadHID(); + // temp = XBR1; + // if (IO_BUFFER.Ptr[2+gOffset] & 1) // enable weak pullups in case GP pins are open-collector and not connected to anything (which would falsely give '0') + // { + // XBR1 &= 0x7F; + // Timer0_Init(HALFMS); + // T0_Wait(2); + // } + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0+gOffset] = 0; // error status + // IO_BUFFER.Ptr[1+gOffset] = P1; // P1 HI (1) or LO (0) + // IO_BUFFER.Ptr[2+gOffset] = P2; // P2 HI (1) or LO (0) + // XBR1 = temp; + // SendPacket(); // no need to check for internal call since only the GUI will request a read through this function + P1 = IOBuf[2]; + P2 = IOBuf[3]; + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 4 OUT = I2C configuration </para> + /// (IO_BUFFER.Ptr[3+gOffset] & 1) ? (SMB0CF |= 0x10) : (SMB0CF &= 0xEF) + /// </summary> + /// <param name="gI2Cflags">bit0 == 1: repeated start</param> + /// <param name="EXTHOLD">set EXTHOLD bit (SMBus setup / hold time extension)</param> + /// <param name="ClearSDAbyTogglingSCL">clear SDA by toggling SCL</param> + public void I2CConfigSet(byte gI2Cflags, byte EXTHOLD, byte ClearSDAbyTogglingSCL) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers I2CConfigSet(byte gI2Cflags, byte EXTHOLD, byte ClearSDAbyTogglingSCL) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in I2CConfigSet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 4; // case (4): //I2C configuration -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 0; // case (0): // write + IOBuf[3] = gI2Cflags; // gI2Cflags = IO_BUFFER.Ptr[2+gOffset]; + // // bit0 == 1: repeated start (versus stop/start) after write before read (applies to random read only) + // // bit1 == 1: start random read with a write, but end the write right away without sending reg address (emulate Jungo dongle for debug purposes) + // // bit2 == 1: repeat transaction if slave NACKs (suggest not to use this) + // // all flags are OR'd + IOBuf[4] = EXTHOLD; // (IO_BUFFER.Ptr[3+gOffset] & 1) ? (SMB0CF |= 0x10) : (SMB0CF &= 0xEF); // set EXTHOLD bit (SMBus setup / hold time extension) + IOBuf[5] = ClearSDAbyTogglingSCL; // if (IO_BUFFER.Ptr[4+gOffset] & 1) // clear SDA by toggling SCL + // {s + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0+gOffset] = 0; // transaction error status + // IO_BUFFER.Ptr[1+gOffset] = clearSDA(); // clearSDA error status + // SendPacket(); // send status of clearing SDA to host + // } + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 4 IN = I2C configuration </para> + /// (IO_BUFFER.Ptr[3+gOffset] & 1) ? (SMB0CF |= 0x10) : (SMB0CF &= 0xEF) + /// </summary> + /// <param name="gI2Cflags">bit0 == 1: repeated start</param> + /// <param name="EXTHOLD">SMB0CF & 0x10</param> + public void I2CConfigGet(out byte gI2Cflags, out byte EXTHOLD) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers I2CConfigGet(out byte gI2Cflags, out byte EXTHOLD) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in I2CConfigGet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 4; // case (4): //I2C configuration -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 1; // case (1): // read + writeReadHID(); + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0+gOffset] = 0; // error status + // IO_BUFFER.Ptr[1+gOffset] = gI2Cflags; + // IO_BUFFER.Ptr[2+gOffset] = (SMB0CF & 0x10) >> 4; // EXTHOLD + gI2Cflags = IOBuf[2]; + EXTHOLD = IOBuf[3]; + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 5 OUT = I2C clock rate (number of counts to overflow) </para> + /// <para>SCL_kHz = TimerClockMHz * 1000 / ReloadTH1</para> + /// </summary> + /// <param name="ReloadTH1">SMBus timer's count value: ReloadTH1 = (byte)(0.5 + (TimerClockMHz * 1000 / SCL_kHz))</param> + public void I2CClockSet(byte ReloadTH1) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers I2CClockSet(byte ReloadTH1) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in I2CClockSet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 5; // case (5): //I2C clock rate (number of counts to overflow) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 0; // case (0): // write + IOBuf[3] = ReloadTH1; + // gSMBusClkFreq = gTimer1ClkFreq / 3.0 / IO_BUFFER.Ptr[2+gOffset]; //GUI sends the number of counts to overflow; HID must calculate the desired SMBus clock frequency + // TR1 = 0; + // Timer1_Init(); + writeHID(); + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 5 IN = I2C clock rate (number of counts to overflow) </para> + /// <para>SCL_kHz = TimerClockMHz * 1000 / ReloadTH1</para> + /// </summary> + /// <param name="TimerClockMHz">SMBus timer's clock frequency (in MHz); expect constant 8</param> + /// <param name="ReloadTH1">SMBus timer's count value: SCL_kHz = TimerClockMHz * 1000 / ReloadTH1</param> + public void I2CClockGet(out byte TimerClockMHz, out byte ReloadTH1) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers I2CClockGet(out byte TimerClockMHz, out byte ReloadTH1) + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in I2CClockGet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 5; // case (5): //I2C clock rate (number of counts to overflow) -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = 1; // case (1): // read + writeReadHID(); + // case (1): // read -- myHID.I2CClockGet(out TimerClockMHz, out ReloadTH1) + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0+gOffset] = 0; // error status + // IO_BUFFER.Ptr[1+gOffset] = gTimer1ClkFreq / 1000000 / 3; //return the SMBus timer's clock frequency (in MHz) + // IO_BUFFER.Ptr[2+gOffset] = 256-TH1; //and return the SMBus timer's count value in order to calculate the SMBus clock frequency; TH1 is the reload value that gets loaded into TL0 upon overflow; the reload value is 256-TH1 since (0)-TH1 gives the proper number of counts to overflow + // SendPacket(); // no need to check for internal call since only the GUI will request a read through this function + // break; + TimerClockMHz = IOBuf[2]; + ReloadTH1 = IOBuf[3]; + // todo: OS24EVK-24 how determine SCL from TimerClockMHZ? Observed I2CClockGet() TimerClockMHz = 8 when SCL = 400kHz. + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 6 OUT = I2C transaction </para> + /// + /// </summary> + /// <param name="deviceAddress">I2C device address, 8-bit left-justified (LSB=R/w bit)</param> + /// <param name="numDataBytes"></param> + /// <param name="numRegBytes"></param> + /// <param name="data"></param> + /// <param name="reg"></param> + /// <param name="ignoreNACK"></param> + public void I2CWrite(byte deviceAddress, byte numDataBytes, byte numRegBytes, byte[] data, byte[] reg, bool ignoreNACK = false) + { + // https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers + // alias of existing function + // IOBuf[1] = 6; // case (6): // I2C transaction -- HID.cs void writeI2C() readI2C() -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + writeI2C(deviceAddress, numDataBytes, numRegBytes, data, reg, ignoreNACK); + } + + /// <summary> + /// <para>Firmware command code 6 IN = I2C transaction </para> + /// + /// </summary> + /// <param name="deviceAddress">I2C device address, 8-bit left-justified (LSB=R/w bit)</param> + /// <param name="numDataBytes"></param> + /// <param name="numRegBytes"></param> + /// <param name="readData"></param> + /// <param name="reg"></param> + /// <param name="ignoreNACK"></param> + /// <returns>status value; may be HID.I2C_NACK_ERROR if ignoreNACK parameter is true</returns> + public int I2CRead(byte deviceAddress, byte numDataBytes, byte numRegBytes, byte[] readData, byte[] reg, bool ignoreNACK = false) + { + // https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers + // alias of existing function + // IOBuf[1] = 6; // case (6): // I2C transaction -- HID.cs void writeI2C() readI2C() -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + return readI2C(deviceAddress, numDataBytes, numRegBytes, readData, reg, ignoreNACK); + } + + /// <summary> + /// <para>Firmware command code 7 OUT = SPI config </para> + /// ((SPI0CFG & 0x30) >> 4) + (SPI0CN & 4) + /// </summary> + /// <param name="config"></param> + public void SPIConfigSet(byte config) + { + // todo: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers SPIConfigSet(byte config) + // Although the MAX30101EVKIT firmware will accept HID SPI commands, this firmware doesn't support SPI interface. + // IOBuf[1] = 7; // case (7): // SPI config -- HID.cs void writeI2C() readI2C() -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + } + + /// <summary> + /// <para>Firmware command code 7 IN = SPI config </para> + /// ((SPI0CFG & 0x30) >> 4) + (SPI0CN & 4) + /// </summary> + /// <param name="config"></param> + public void SPIConfigGet(out byte config) + { + // todo: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers SPIConfigGet(out byte config) + // Although the MAX30101EVKIT firmware will accept HID SPI commands, this firmware doesn't support SPI interface. + // IOBuf[1] = 7; // case (7): // SPI config -- HID.cs void writeI2C() readI2C() -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + config = 0; + } + + /// <summary> + /// <para>Firmware command code 8 OUT = SPI clock rate </para> + /// </summary> + /// <param name="SPI0CKR"></param> + public void SPIClockSet(byte SPI0CKR) + { + // todo: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers SPIClockSet(byte SPI0CKR) + // Although the MAX30101EVKIT firmware will accept HID SPI commands, this firmware doesn't support SPI interface. + // IOBuf[1] = 8; // case (8): // SPI clock rate -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + } + + /// <summary> + /// <para>Firmware command code 8 IN = SPI clock rate </para> + /// </summary> + /// <param name="SPI0CKR"></param> + public void SPIClockGet(out byte SPI0CKR) + { + // todo: https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers SPIClockGet(out byte SPI0CKR) + // Although the MAX30101EVKIT firmware will accept HID SPI commands, this firmware doesn't support SPI interface. + // IOBuf[1] = 8; // case (8): // SPI clock rate -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + SPI0CKR = 0; + } + + /// <summary> + /// <para>Firmware command code 9 = SPI transaction </para> + /// </summary> + /// <param name="mosiData">SPI MOSI (Master-Out, Slave-In) data to write into slave device</param> + /// <param name="misoBuffer">SPI MISO (Master-In, Slave-Out) data buffer containing data bytes received from slave device + /// (size will be allocated same size as mosiData)</param> + public void SPITransfer(byte[] mosiData, out byte[] misoBuffer) + { + // verify: https://jira.maxim-ic.com/browse/OS24EVK-24 SPITransfer(byte[] mosiData, out byte[] misoBuffer) + // Although the MAX30101EVKIT firmware will accept HID SPI commands, this firmware doesn't support SPI interface. + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in I2CConfigSet + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + // assign default out values in case of failure + int byteCount = mosiData.Length; + byte num_bytes = (byte)(byteCount & 0xFF); + misoBuffer = new byte[byteCount]; + for (int byteIndex = 0; byteIndex < byteCount; byteIndex++) + { + // initial dummy data + misoBuffer[byteIndex] = 0x55; + } + IOBuf[1] = 9; // case (9): // SPI transaction -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + //IOBuf[8] = x; // IO_BUFFER.Ptr[7+gOffset] -- first byte of data starts on the (7 + offset) byte + for (uint byteIndex = 0; byteIndex < num_bytes; byteIndex++) + { + IOBuf[8 + byteIndex] = (byte)(mosiData[byteIndex]); + } + //switch (SPImode) + //{ + // case 0: + IOBuf[2] = 0; // IO_BUFFER.Ptr[1+gOffset] -- 0 for SPI_mode0: multi-byte SPI transfer. + IOBuf[3] = 0; // IO_BUFFER.Ptr[2+gOffset] -- not used + IOBuf[4] = 0; // IO_BUFFER.Ptr[3+gOffset] -- phase_change // !=0 enable changing the clock phase. some slaves change phase between write/read + IOBuf[5] = 0; // IO_BUFFER.Ptr[4+gOffset] -- phase_change_byte // byte index where phase change should happen + IOBuf[6] = num_bytes; // IO_BUFFER.Ptr[5+gOffset] -- num_bytes + IOBuf[7] = 0; // IO_BUFFER.Ptr[6+gOffset] -- not used + // IO_BUFFER.Ptr[7+gOffset] -- first byte of data starts on the (7 + offset) byte + // SPI_mode0(IO_BUFFER.Ptr[3 + gOffset], IO_BUFFER.Ptr[4 + gOffset], IO_BUFFER.Ptr[5 + gOffset], IO_BUFFER.Ptr); + // break; + //case 1: + // IOBuf[2] = 1; // IO_BUFFER.Ptr[1+gOffset] -- 1 for SPI_mode1: two-byte SPI transfer. read flag: enable changing the clock phase on first byte and changing phase between first and second byte + // IOBuf[3] = 0; // IO_BUFFER.Ptr[2+gOffset] -- read flag + // // IO_BUFFER.Ptr[3+gOffset] -- not used + // // IO_BUFFER.Ptr[4+gOffset] -- not used + // // IO_BUFFER.Ptr[5+gOffset] -- not used; num_bytes = 2 + // // IO_BUFFER.Ptr[6+gOffset] -- not used + // // IO_BUFFER.Ptr[7+gOffset] -- first byte of data starts on the (7 + offset) byte + // // SPI_mode1(temp, IO_BUFFER.Ptr); + // break; + //case 2: + // IOBuf[2] = 2; // IO_BUFFER.Ptr[1+gOffset] --2 for SPI_mode2: two-byte SPI transfer. read flag: enable changing the clock phase on first byte and sampling the LSb after the second byte read + // IOBuf[3] = 0; // IO_BUFFER.Ptr[2+gOffset] -- read flag + // // IO_BUFFER.Ptr[3+gOffset] -- not used + // // IO_BUFFER.Ptr[4+gOffset] -- not used + // // IO_BUFFER.Ptr[5+gOffset] -- not used; num_bytes = 2 + // // IO_BUFFER.Ptr[6+gOffset] -- not used + // // IO_BUFFER.Ptr[7+gOffset] -- first byte of data starts on the (7 + offset) byte + // // SPI_mode2(temp, IO_BUFFER.Ptr); + // break; + //} + writeHID(); + // IO_BUFFER.Ptr[0] = SHORT_REPORT_ID; + // IO_BUFFER.Ptr[0+gOffset] = 0; // error status + // IO_BUFFER.Ptr[1+gOffset] = gI2Cflags; + // IO_BUFFER.Ptr[2+gOffset] = (SMB0CF & 0x10) >> 4; // EXTHOLD + //gI2Cflags = IOBuf[2]; + //EXTHOLD = IOBuf[3]; + for (uint byteIndex = 0; byteIndex < byteCount; byteIndex++) + { + misoBuffer[byteIndex] = IOBuf[byteIndex + 2]; + } + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + + /// <summary> + /// <para>Firmware command code 23 OUT = enable INT0 (or Mock HID FIFO data diagnostic) </para> + /// <para> + /// - INT0Enable(0) disabled (EX0=0) + /// - INT0Enable(1) real hardware (EX0=1) + /// - INT0Enable(2) Mock HID x1 channel 101, 102, 103, ... + /// - INT0Enable(3) Mock HID x2 channels 101, 201, 102, 202, 103, 203, ... + /// - INT0Enable(4) Mock HID x3 channels 101, 201, 301, 102, 202, 302, 103, 203, 303, ... + /// - INT0Enable(5) Mock HID x4 channels 101, 201, 301, 401, 102, 202, 302, 402, 103, 203, 303, 403, ... + /// </para> + /// </summary> + /// <param name="EX0"></param> + public void INT0Enable(byte EX0) + { + // https://jira.maxim-ic.com/browse/OS24EVK-24 Refactor: HID report enum and wrappers + // reset report counter in the HID and enable INT0 + // verify: https://jira.maxim-ic.com/browse/OS24EVK-57 mutex lock HID.IOBuf[] in INT0Enable + mutexGuardIOBuf.WaitOne(); // Wait until it is safe to enter. + FlushQueue(); + Array.Clear(IOBuf, 0, IOBuf.Length); + reportID(); + IOBuf[1] = 23; // enable INT0 -- see F3xx_USB0_ReportHandler.c void OUT_REPORT_HANDLER(int internalCall) + IOBuf[2] = EX0; // reportTypeFlag; // 1; + writeHID(); + _firmwareINT0Enabled = EX0; + mutexGuardIOBuf.ReleaseMutex(); // Release the Mutex. + } + private byte _firmwareINT0Enabled = 0; + public byte FirmwareINT0Enabled { get { return _firmwareINT0Enabled; } } + + /// <summary> + /// Search for a device attached to I2C bus, + /// given a list of poossible device addresses, + /// and a constant device ID register to test. + /// </summary> + /// <param name="I2C_DeviceAddressList_8bitLeftJustified">List of possible I2C device addresses to test. I2C device addresses are 8-bit left-justified (LSB=R/w bit)</param> + /// <param name="DeviceId_RegAddress">device register address of a constant "Device ID" register</param> + /// <param name="DeviceId_RegValue_Expect">register value of the constant "Device ID" register</param> + /// <returns>I2C device addresses, or 0 if not found</returns> + public byte SearchI2CdeviceAddressList(byte[] I2C_DeviceAddressList_8bitLeftJustified, byte DeviceId_RegAddress, byte DeviceId_RegValue_Expect) + { + // https://jira.maxim-ic.com/browse/OS24EVK-57 accelerometer support: SearchI2CdeviceAddressList optional I2C device + foreach (byte test_I2C_Address in I2C_DeviceAddressList_8bitLeftJustified) + { + //try + //{ + byte[] data = new byte[2]; + byte[] reg = new byte[1]; + reg[0] = (byte)DeviceId_RegAddress; + // readI2C should already take care of mutex lock / unlock + // https://jira.maxim-ic.com/browse/OS24EVK-59 avoid NACK exception in SearchI2CdeviceAddressList + bool ignoreNACK = true; + int status = readI2C(test_I2C_Address, 2, 1, data, reg, ignoreNACK); + if (status == HID.I2C_NACK_ERROR) + { + continue; + } + else + { + byte DeviceId_RegValue_Actual = data[0]; + if (DeviceId_RegValue_Actual == DeviceId_RegValue_Expect) + { + return test_I2C_Address; + } + } + //} + //catch (Exception) + //{ + // // myHID.readI2C can throw Exception("invalid I2C address"); + //} + } + return 0; + } + + } +}