Example #1
0
        public byte[] ReadUSB()
        {
            // try reading a few times, in case we lose the connection brieflyl
            for (int tries = 0; tries < 3; ++tries)
            {
                // set up a non-blocking ("overlapped") read
                const int rptLen = 15;
                byte[]    buf    = new byte[rptLen];
                buf[0] = 0x00;
                EventWaitHandle ev = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset);
                ov.OffsetLow   = ov.OffsetHigh = 0;
                ov.EventHandle = ev.SafeWaitHandle.DangerousGetHandle();
                HIDImports.ReadFile(fp, buf, rptLen, IntPtr.Zero, ref ov);

                // Wait briefly for the read to complete.  But don't wait forever - we might
                // be talking to a device interface that doesn't provide the type of status
                // report we're looking for, in which case we don't want to get stuck waiting
                // for something that will never happen.  If this is indeed the controller
                // interface we're interested in, it will respond within a few milliseconds
                // with our status report.
                if (ev.WaitOne(100))
                {
                    // The read completed successfully!  Get the result.
                    UInt32 readLen;
                    if (HIDImports.GetOverlappedResult(fp, ref ov, out readLen, 0) == 0)
                    {
                        // The read failed.  Try re-opening the file handle in case we
                        // dropped the connection, then re-try the whole read.
                        TryReopenHandle();
                        continue;
                    }
                    else if (readLen != rptLen)
                    {
                        // The read length didn't match what we expected.  This might be
                        // a different device (not a Pinscape controller) or a different
                        // version that we don't know how to talk to.  In either case,
                        // return failure.
                        return(null);
                    }
                    else
                    {
                        // The read succeed and was the correct size.  Return the data.
                        return(buf);
                    }
                }
                else
                {
                    // The read timed out.  This must not be our control interface after
                    // all.  Cancel the read and try reopening the handle.
                    HIDImports.CancelIo(fp);
                    if (TryReopenHandle())
                    {
                        continue;
                    }
                    return(null);
                }
            }

            // don't retry more than a few times
            return(null);
        }
Example #2
0
 private IntPtr OpenFile()
 {
     return(HIDImports.CreateFile(
                path, HIDImports.GENERIC_READ_WRITE, HIDImports.SHARE_READ_WRITE,
                IntPtr.Zero, HIDImports.OPEN_EXISTING, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero));
 }
Example #3
0
        // Get a list of connected Pinscape Controller devices
        public static List <PinscapeDev> FindDevices()
        {
            // set up an empty return list
            var devices = new List <PinscapeDev>();

            // get the list of devices matching the HID class GUID
            Guid guid = Guid.Empty;

            HIDImports.HidD_GetHidGuid(out guid);
            IntPtr hdev = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);

            // set up the attribute structure buffer
            HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA();
            diData.cbSize = Marshal.SizeOf(diData);

            // read the devices in our list
            for (uint i = 0;
                 HIDImports.SetupDiEnumDeviceInterfaces(hdev, IntPtr.Zero, ref guid, i, ref diData);
                 ++i)
            {
                // get the size of the detail data structure
                UInt32 size = 0;
                HIDImports.SetupDiGetDeviceInterfaceDetail(hdev, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero);

                // now actually read the detail data structure
                HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA();
                diDetail.cbSize = (IntPtr.Size == 8) ? (uint)8 : (uint)5;
                HIDImports.SP_DEVINFO_DATA devInfoData = new HIDImports.SP_DEVINFO_DATA();
                devInfoData.cbSize = Marshal.SizeOf(devInfoData);
                if (HIDImports.SetupDiGetDeviceInterfaceDetail(hdev, ref diData, ref diDetail, size, out size, out devInfoData))
                {
                    // create a file handle to access the device
                    IntPtr fp = HIDImports.CreateFile(
                        diDetail.DevicePath, HIDImports.GENERIC_READ_WRITE, HIDImports.SHARE_READ_WRITE,
                        IntPtr.Zero, HIDImports.OPEN_EXISTING, 0, IntPtr.Zero);

                    // read the attributes
                    HIDImports.HIDD_ATTRIBUTES attrs = new HIDImports.HIDD_ATTRIBUTES();
                    attrs.Size = Marshal.SizeOf(attrs);
                    if (HIDImports.HidD_GetAttributes(fp, ref attrs))
                    {
                        // read the product name string
                        String name    = "<unknown>";
                        byte[] nameBuf = new byte[128];
                        if (HIDImports.HidD_GetProductString(fp, nameBuf, 128))
                        {
                            name = System.Text.Encoding.Unicode.GetString(nameBuf).TrimEnd('\0');
                        }

                        // if the vendor and product ID match an LedWiz, and the
                        // product name contains "pinscape", it's one of ours
                        PinscapeDev di;
                        if (CheckIDMatch(attrs) &&
                            Regex.IsMatch(name, @"\b(?i)pinscape controller\b") &&
                            (di = PinscapeDev.Create(
                                 diDetail.DevicePath, name, attrs.VendorID, attrs.ProductID, attrs.VersionNumber)) != null)
                        {
                            // add the device to our list
                            devices.Add(di);
                        }

                        // done with the file handle
                        if (fp.ToInt32() != 0 && fp.ToInt32() != -1)
                        {
                            HIDImports.CloseHandle(fp);
                        }
                    }
                }
            }

            // done with the device info list
            HIDImports.SetupDiDestroyDeviceInfoList(hdev);

            // return the device list
            return(devices);
        }
Example #4
0
        private PinscapeDev(string path, string name, ushort vendorID, ushort productID, ushort version)
        {
            // remember the settings
            this.path            = path;
            this.name            = name;
            this.vendorID        = (ushort)vendorID;
            this.productID       = (ushort)productID;
            this.version         = version;
            this.JoystickEnabled = false;
            this.fp = OpenFile();

            // presume invalid
            this.isValid = false;

            // read a status report
            byte[] buf = ReadUSB();
            if (buf != null)
            {
                // successfully read a report - mark it as valid
                isValid = true;
            }

            // Check the HID interface to see if the HID Usage type is
            // type 4, for Joystick.  If so, the joystick interface is
            // enabled, which the device uses to send nudge and plunger
            // readings.  If not, the joystick interface is disabled, so
            // the device only sends private status messages and query
            // responses.
            IntPtr ppdata;

            if (HIDImports.HidD_GetPreparsedData(this.fp, out ppdata))
            {
                // Check the usage.  If the joystick is enabled, the
                // usage will be 4 = Joystick (on usage page 1, "generic
                // desktop").  If not, the usage is 0 = Undefined, indicating
                // our private status and query interface.
                HIDImports.HIDP_CAPS caps;
                HIDImports.HidP_GetCaps(ppdata, out caps);
                if (caps.UsagePage == 1 && caps.Usage == 4)
                {
                    this.JoystickEnabled = true;
                }

                // If the usage is page 1, usage 6, it's a keyboard interface.
                // This type of interface will be exposed alongside the joystick
                // or private interface if any keyboard input is enabled.   Ignore
                // the keyboard interface, since it doesn't accept any control
                // commands - those are strictly for the LedWiz output endpoint
                // that's associated with the joystick or private interface.
                if (caps.UsagePage == 1 && caps.Usage == 6)
                {
                    isValid = false;
                }

                // free the preparsed data
                HIDImports.HidD_FreePreparsedData(ppdata);
            }

            // figure the LedWiz unit number
            LedWizUnitNo = (vendorID == 0xFAFA ? ((productID & 0x0F) + 1) : 0);
        }