CORE-POS/IS4C

View on GitHub
pos/is4c-nf/scale-drivers/drivers/NewMagellan/USB-Win32.cs

Summary

Maintainability
B
4 hrs
Test Coverage
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace USBLayer {

public class USBWrapper_Win32 : USBWrapper {
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    protected struct Overlapped {
        public uint Internal;
        public uint InternalHigh;
        public uint Offset;
        public uint OffsetHigh;
        public IntPtr Event;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    protected struct DeviceInterfaceData {
        public int Size;
        public Guid InterfaceClassGuid;
        public int Flags;
        public IntPtr Reserved;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    protected struct HidCaps {
        public short Usage;
        public short UsagePage;
        public short InputReportByteLength;
        public short OutputReportByteLength;
        public short FeatureReportByteLength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
        public short[] Reserved;
        public short NumberLinkCollectionNodes;
        public short NumberInputButtonCaps;
        public short NumberInputValueCaps;
        public short NumberInputDataIndices;
        public short NumberOutputButtonCaps;
        public short NumberOutputValueCaps;
        public short NumberOutputDataIndices;
        public short NumberFeatureButtonCaps;
        public short NumberFeatureValueCaps;
        public short NumberFeatureDataIndices;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct DeviceInterfaceDetailData {
        public int Size;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string DevicePath;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
    public class DeviceBroadcastInterface {
        public int Size;
        public int DeviceType;
        public int Reserved;
        public Guid ClassGuid;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string Name;
    }        

    public const int WM_DEVICECHANGE = 0x0219;
    public const int DEVICE_ARRIVAL = 0x8000;
    public const int DEVICE_REMOVECOMPLETE = 0x8004;
    protected const int DIGCF_PRESENT = 0x02;
    protected const int DIGCF_DEVICEINTERFACE = 0x10;
    protected const int DEVTYP_DEVICEINTERFACE = 0x05;
    protected const int DEVICE_NOTIFY_WINDOW_HANDLE = 0;
    protected const uint PURGE_TXABORT = 0x01;
    protected const uint PURGE_RXABORT = 0x02;
    protected const uint PURGE_TXCLEAR = 0x04;
    protected const uint PURGE_RXCLEAR = 0x08;
    protected const uint GENERIC_READ = 0x80000000;
    protected const uint GENERIC_WRITE = 0x40000000;
    protected const uint FILE_FLAG_OVERLAPPED = 0x40000000;
    protected const uint OPEN_EXISTING = 3;
    protected const uint ERROR_IO_PENDING = 997;
    protected const uint INFINITE = 0xFFFFFFFF;
    public static IntPtr NullHandle = IntPtr.Zero;
    protected static IntPtr InvalidHandleValue = new IntPtr(-1);

    private IntPtr native_handle;
    private SafeFileHandle safe_handle;

    [DllImport("hid.dll",      SetLastError = true)] protected static extern void HidD_GetHidGuid(out Guid gHid);
    [DllImport("setupapi.dll", SetLastError = true)] protected static extern IntPtr SetupDiGetClassDevs(ref Guid gClass, [MarshalAs(UnmanagedType.LPStr)] string strEnumerator, IntPtr hParent, uint nFlags);
    [DllImport("setupapi.dll", SetLastError = true)] protected static extern int SetupDiDestroyDeviceInfoList(IntPtr lpInfoSet);
    [DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiEnumDeviceInterfaces(IntPtr lpDeviceInfoSet, uint nDeviceInfoData, ref Guid gClass, uint nIndex, ref DeviceInterfaceData oInterfaceData);
    [DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr lpDeviceInfoSet, ref DeviceInterfaceData oInterfaceData, IntPtr lpDeviceInterfaceDetailData, uint nDeviceInterfaceDetailDataSize, ref uint nRequiredSize, IntPtr lpDeviceInfoData);
    [DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr lpDeviceInfoSet, ref DeviceInterfaceData oInterfaceData, ref DeviceInterfaceDetailData oDetailData, uint nDeviceInterfaceDetailDataSize, ref uint nRequiredSize, IntPtr lpDeviceInfoData);
    [DllImport("user32.dll",   SetLastError = true)] protected static extern IntPtr RegisterDeviceNotification(IntPtr hwnd, DeviceBroadcastInterface oInterface, uint nFlags);
    [DllImport("user32.dll",   SetLastError = true)] protected static extern bool UnregisterDeviceNotification(IntPtr hHandle);
    [DllImport("hid.dll",      SetLastError = true)] protected static extern bool HidD_GetPreparsedData(IntPtr hFile, out IntPtr lpData);
    [DllImport("hid.dll",      SetLastError = true)] protected static extern bool HidD_FreePreparsedData(ref IntPtr pData);
    [DllImport("hid.dll",      SetLastError = true)] protected static extern int HidP_GetCaps(IntPtr lpData, out HidCaps oCaps);
    [DllImport("kernel32.dll", SetLastError = true)] protected static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)] string strName, uint nAccess, uint nShareMode, IntPtr lpSecurity, uint nCreationFlags, uint nAttributes, IntPtr lpTemplate);
    [DllImport("kernel32.dll", SetLastError = true)] protected static extern int CloseHandle(IntPtr hFile);

    /**
     * Get a handle for USB device file
     * @param filename the name of the file OR vendor and device ids formatted as "vid&pid"
     * @param report_size [optional] report size in bytes
     * @return open read/write Stream
     */
    public override Stream GetUSBHandle(string filename, int report_size){
        if (filename.IndexOf("&") > 0){
            String[] parts = filename.Split(new Char[]{'&'});
            if (parts.Length != 2){
                System.Console.WriteLine(filename);
                return null;
            }
            try {
                filename = GetDeviceFilename(Convert.ToInt32(parts[0]),Convert.ToInt32(parts[1]));
            }
            catch(Exception ex){
                System.Console.WriteLine(ex.ToString());
                return null;
            }
        }
        native_handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, IntPtr.Zero);
        if (native_handle != InvalidHandleValue){
            IntPtr lpData;
            if (HidD_GetPreparsedData(native_handle, out lpData)){
                HidCaps oCaps;
                HidP_GetCaps(lpData, out oCaps);    // extract the device capabilities from the internal buffer
                int inp = oCaps.InputReportByteLength;    // get the input...
                int outp = oCaps.OutputReportByteLength;    // ... and output report length
                HidD_FreePreparsedData(ref lpData);
                System.Console.WriteLine("Input: {0}, Output: {1}",inp, outp);
            }
            safe_handle = new SafeFileHandle(native_handle, true);
            return new FileStream(safe_handle, FileAccess.Read | FileAccess.Write, report_size, true);
            // old method that works but generates obsolete warnings
            // return new FileStream(native_handle, FileAccess.Read | FileAccess.Write, true, report_size, true);    
        }
        return null;
    }

    public override void CloseUSBHandle(){
        try {
            safe_handle.Close();
            CloseHandle(native_handle);
        }
        catch(Exception){}
    }

    private String GetDeviceFilename(int vid, int pid){
        string devname = string.Format("vid_{0:x4}&pid_{1:x4}", vid, pid); 
        string filename = null;

        Guid gHid;
        HidD_GetHidGuid(out gHid);

        IntPtr hInfoSet = SetupDiGetClassDevs(ref gHid, null, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);

                DeviceInterfaceData oInterface = new DeviceInterfaceData();
                oInterface.Size = Marshal.SizeOf(oInterface);
        int nIndex = 0;
        while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface)) {
            string strDevicePath = GetDevicePath(hInfoSet, ref oInterface);    
            System.Console.WriteLine(strDevicePath);
            if (strDevicePath.IndexOf(devname) >= 0){
                filename = strDevicePath;
                break;
            }
            nIndex++;
        }

                SetupDiDestroyDeviceInfoList(hInfoSet);

        return filename;
    }

    private static string GetDevicePath(IntPtr hInfoSet, ref DeviceInterfaceData oInterface){
        uint nRequiredSize = 0;
        if (!SetupDiGetDeviceInterfaceDetail(hInfoSet, ref oInterface, IntPtr.Zero, 0, ref nRequiredSize, IntPtr.Zero)) {
            DeviceInterfaceDetailData oDetail = new DeviceInterfaceDetailData();
            oDetail.Size = (IntPtr.Size==8) ? 8 : 5;
            if (SetupDiGetDeviceInterfaceDetail(hInfoSet, ref oInterface, ref oDetail, nRequiredSize, ref nRequiredSize, IntPtr.Zero)){
                return oDetail.DevicePath;
            }
        }
        return null;
    }

} // end class

} // end namespace