This is the latest working repository used in our demo video for the Maxim to display temperature readings on Bluetooth
hspguisourcev301/HspGuiSourceV301/HSPGui/HID.cs
- Committer:
- darienf
- Date:
- 2021-04-10
- Revision:
- 3:36de8b9e4b1a
File content as of revision 3:36de8b9e4b1a:
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; } } }