示例#1
0
            public byte[] GetReportDescriptor()
            {
                var bytes = new List <byte>();

                EncodedItem.EncodeItems(_items, bytes);
                return(bytes.ToArray());
            }
示例#2
0
        private static HidDevice GetFromUsages(IEnumerable <HidDevice> devices, uint usagePage, uint usage)
        {
            foreach (var dev in devices)
            {
                try
                {
                    var raw    = dev.GetRawReportDescriptor();
                    var usages = EncodedItem.DecodeItems(raw, 0, raw.Length).Where(t => t.TagForGlobal == GlobalItemTag.UsagePage);

                    if (usages.Any(g => g.ItemType == ItemType.Global && g.DataValue == usagePage))
                    {
                        if (usages.Any(l => l.ItemType == ItemType.Local && l.DataValue == usage))
                        {
                            return(dev);
                        }
                    }
                }
                catch
                {
                    //failed to get the report descriptor, skip
                }
            }

            return(null);
        }
        /// <summary>
        /// Parses a raw HID report descriptor.
        /// </summary>
        /// <param name="buffer">The buffer containing the report descriptor.</param>
        void ParseRawReportDescriptor(byte[] buffer)
        {
            Throw.If.Null(buffer, "buffer");

            var items = EncodedItem.DecodeItems(buffer, 0, buffer.Length);

            ParseEncodedItems(items);
        }
        /// <summary>
        /// Parses a single <see cref="EncodedItem"/>.
        /// Call this repeatedly for every item to completely decode a report descriptor.
        /// </summary>
        /// <param name="item">The item to parse.</param>
        public void Parse(EncodedItem item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            uint value = item.DataValue;

            switch (item.Type)
            {
            case ItemType.Main:
                ParseMain(item.TagForMain, value);
                LocalItemState.Clear();
                break;

            case ItemType.Local:
                switch (item.TagForLocal)
                {
                case LocalItemTag.Usage:
                case LocalItemTag.UsageMinimum:
                case LocalItemTag.UsageMaximum:
                    if (value <= 0xffff)
                    {
                        value |= GetGlobalItemValue(GlobalItemTag.UsagePage) << 16;
                    }
                    break;
                }
                LocalItemState.Add(new KeyValuePair <LocalItemTag, uint> (item.TagForLocal, value));
                break;

            case ItemType.Global:
                switch (item.TagForGlobal)
                {
                case GlobalItemTag.Push:
                    GlobalItemStateStack.Add(new Dictionary <GlobalItemTag, EncodedItem> (GlobalItemState));
                    break;

                case GlobalItemTag.Pop:
                    GlobalItemStateStack.RemoveAt(GlobalItemState.Count - 1);
                    break;

                default:
                    switch (item.TagForGlobal)
                    {
                    case GlobalItemTag.ReportID:
                        ReportsUseID = true;
                        break;
                    }

                    GlobalItemState[item.TagForGlobal] = item;
                    break;
                }
                break;
            }
        }
        void ParseDataMain(MainItemTag tag, uint value, out LocalIndexes indexes)
        {
            ReportSegment segment = new ReportSegment();

            segment.Flags        = (DataMainItemFlags)value;
            segment.Parent       = CurrentCollection;
            segment.ElementCount = (int)GetGlobalItemValue(GlobalItemTag.ReportCount);
            segment.ElementSize  = (int)GetGlobalItemValue(GlobalItemTag.ReportSize);
            segment.Unit         = new Units.Unit(GetGlobalItemValue(GlobalItemTag.Unit));
            segment.UnitExponent = Units.Unit.DecodeExponent(GetGlobalItemValue(GlobalItemTag.UnitExponent));
            indexes = segment.Indexes;

            EncodedItem logicalMinItem = GetGlobalItem(GlobalItemTag.LogicalMinimum);
            EncodedItem logicalMaxItem = GetGlobalItem(GlobalItemTag.LogicalMaximum);

            segment.LogicalIsSigned =
                (logicalMinItem != null && logicalMinItem.DataValueMayBeNegative) ||
                (logicalMaxItem != null && logicalMaxItem.DataValueMayBeNegative);
            int logicalMinimum  = logicalMinItem == null ? 0 : segment.LogicalIsSigned ? logicalMinItem.DataValueSigned : (int)logicalMinItem.DataValue;
            int logicalMaximum  = logicalMaxItem == null ? 0 : segment.LogicalIsSigned ? logicalMaxItem.DataValueSigned : (int)logicalMaxItem.DataValue;
            int physicalMinimum = (int)GetGlobalItemValue(GlobalItemTag.PhysicalMinimum);
            int physicalMaximum = (int)GetGlobalItemValue(GlobalItemTag.PhysicalMaximum);

            if (!IsGlobalItemSet(GlobalItemTag.PhysicalMinimum) ||
                !IsGlobalItemSet(GlobalItemTag.PhysicalMaximum) ||
                (physicalMinimum == 0 && physicalMaximum == 0))
            {
                physicalMinimum = logicalMinimum;
                physicalMaximum = logicalMaximum;
            }

            segment.LogicalMinimum  = logicalMinimum;
            segment.LogicalMaximum  = logicalMaximum;
            segment.PhysicalMinimum = physicalMinimum;
            segment.PhysicalMaximum = physicalMaximum;

            Report     report;
            ReportType reportType
                = tag == MainItemTag.Output ? ReportType.Output
                : tag == MainItemTag.Feature ? ReportType.Feature
                : ReportType.Input;
            uint reportID = GetGlobalItemValue(GlobalItemTag.ReportID);

            if (!TryGetReport(reportType, (byte)reportID, out report))
            {
                report = new Report()
                {
                    ID   = (byte)reportID,
                    Type = reportType
                };
                Reports.Add(report);
            }
            segment.Report = report;
        }
示例#6
0
        /// <summary>
        /// Parses a single <see cref="EncodedItem"/>.
        /// Call this repeatedly for every item to completely decode a report descriptor.
        /// </summary>
        /// <param name="item">The item to parse.</param>
        void ParseEncodedItem(EncodedItem item)
        {
            Throw.If.Null(item, "item");
            uint value = item.DataValue;

            switch (item.ItemType)
            {
            case ItemType.Main:
                ParseMain(item.TagForMain, value);
                State.LocalItemState.Clear();
                break;

            case ItemType.Local:
                switch (item.TagForLocal)
                {
                case LocalItemTag.Usage:
                case LocalItemTag.UsageMinimum:
                case LocalItemTag.UsageMaximum:
                    if (value <= 0xffff)
                    {
                        value |= State.GetGlobalItemValue(GlobalItemTag.UsagePage) << 16;
                    }
                    break;
                }
                State.LocalItemState.Add(new KeyValuePair <LocalItemTag, uint>(item.TagForLocal, value));
                break;

            case ItemType.Global:
                switch (item.TagForGlobal)
                {
                case GlobalItemTag.Push:
                    State.GlobalItemStateStack.Add(new Dictionary <GlobalItemTag, EncodedItem>(State.GlobalItemState));
                    break;

                case GlobalItemTag.Pop:
                    State.GlobalItemStateStack.RemoveAt(State.GlobalItemState.Count - 1);
                    break;

                default:
                    switch (item.TagForGlobal)
                    {
                    case GlobalItemTag.ReportID:
                        ReportsUseID = true; break;
                    }

                    State.GlobalItemState[item.TagForGlobal] = item;
                    break;
                }
                break;
            }
        }
示例#7
0
        private static bool VerifyUsageAndUsagePage(this HidDevice device, uint usagePage, uint usage)
        {
            try
            {
                var rawReportDescriptor = device.GetRawReportDescriptor();

                var items = EncodedItem.DecodeItems(rawReportDescriptor, 0, rawReportDescriptor.Length).Where(t => t.TagForGlobal == GlobalItemTag.UsagePage);

                return(items.Any(item => item.ItemType == ItemType.Global && item.DataValue == usagePage) &&
                       items.Any(item => item.ItemType == ItemType.Local && item.DataValue == usage));
            }
            catch
            {
                return(false);
            }
        }
示例#8
0
            public void AddGlobalItemSigned(GlobalItemTag globalItemTag, int dataValue)
            {
                uint oldDataValue;

                if (_globals.TryGetValue(globalItemTag, out oldDataValue) && oldDataValue == (uint)dataValue)
                {
                    return;
                }
                _globals[globalItemTag] = (uint)dataValue;

                var item = new EncodedItem()
                {
                    ItemType = ItemType.Global, TagForGlobal = globalItemTag, DataValueSigned = dataValue
                };

                _items.Add(item);
            }
        public uint GetGlobalItemValue(GlobalItemTag tag)
        {
            EncodedItem item = GetGlobalItem(tag);

            return(item != null ? item.DataValue : 0);
        }
        void ParseMainData(MainItemTag tag, uint value)
        {
            DataItem dataItem = new DataItem();

            dataItem.Flags        = (DataItemFlags)value;
            dataItem.ParentItem   = State.CurrentCollectionItem;
            dataItem.ElementCount = (int)State.GetGlobalItemValue(GlobalItemTag.ReportCount);
            dataItem.ElementBits  = (int)State.GetGlobalItemValue(GlobalItemTag.ReportSize);
            dataItem.Unit         = new Units.Unit(State.GetGlobalItemValue(GlobalItemTag.Unit));
            dataItem.UnitExponent = Units.Unit.DecodeExponent(State.GetGlobalItemValue(GlobalItemTag.UnitExponent));

            EncodedItem logicalMinItem = State.GetGlobalItem(GlobalItemTag.LogicalMinimum);
            EncodedItem logicalMaxItem = State.GetGlobalItem(GlobalItemTag.LogicalMaximum);

            dataItem.IsLogicalSigned = !dataItem.IsArray && ((logicalMinItem != null ? logicalMinItem.DataValue : 0) > (logicalMaxItem != null ? logicalMaxItem.DataValue : 0));
            int logicalMinimum = logicalMinItem == null ? 0 : dataItem.IsLogicalSigned ? logicalMinItem.DataValueSigned : (int)logicalMinItem.DataValue;
            int logicalMaximum = logicalMaxItem == null ? 0 : dataItem.IsLogicalSigned ? logicalMaxItem.DataValueSigned : (int)logicalMaxItem.DataValue;

            EncodedItem physicalMinItem  = State.GetGlobalItem(GlobalItemTag.PhysicalMinimum);
            EncodedItem physicalMaxItem  = State.GetGlobalItem(GlobalItemTag.PhysicalMaximum);
            bool        isPhysicalSigned = !dataItem.IsArray && ((physicalMinItem != null ? physicalMinItem.DataValue : 0) > (physicalMaxItem != null ? physicalMaxItem.DataValue : 0));
            int         physicalMinimum  = physicalMinItem == null ? 0 : isPhysicalSigned ? physicalMinItem.DataValueSigned : (int)physicalMinItem.DataValue;
            int         physicalMaximum  = physicalMaxItem == null ? 0 : isPhysicalSigned ? physicalMaxItem.DataValueSigned : (int)physicalMaxItem.DataValue;

            if (physicalMinimum == 0 && physicalMaximum == 0)
            {
                physicalMinimum = logicalMinimum; physicalMaximum = logicalMaximum;
            }

            dataItem.LogicalMinimum     = logicalMinimum; dataItem.LogicalMaximum = logicalMaximum;
            dataItem.RawPhysicalMinimum = physicalMinimum; dataItem.RawPhysicalMaximum = physicalMaximum;

            Report     report;
            ReportType reportType
                = tag == MainItemTag.Output ? ReportType.Output
                : tag == MainItemTag.Feature ? ReportType.Feature
                : ReportType.Input;
            uint reportID = State.GetGlobalItemValue(GlobalItemTag.ReportID);

            if (!TryGetReport(reportType, (byte)reportID, out report))
            {
                report = new Report()
                {
                    ReportID = (byte)reportID, ReportType = reportType
                };
                Reports.Add(report);

                var collection = State.CurrentCollectionItem;
                while (collection != null && !(collection is DeviceItem))
                {
                    collection = collection.ParentItem;
                }
                if (collection is DeviceItem)
                {
                    ((DeviceItem)collection).Reports.Add(report);
                }
            }
            report.DataItems.Add(dataItem);

            ParseMainIndexes(dataItem);
        }
示例#11
0
        static void Main(string[] args)
        {
            HidSharpDiagnostics.EnableTracing       = true;
            HidSharpDiagnostics.PerformStrictChecks = true;

            var list = DeviceList.Local;

            list.Changed += (sender, e) => Console.WriteLine("Device list changed.");

            var allDeviceList = list.GetAllDevices().ToArray();

            Console.WriteLine("All device list:");
            foreach (Device dev in allDeviceList)
            {
                Console.WriteLine(dev.ToString() + " @ " + dev.DevicePath);
            }
            var stopwatch     = Stopwatch.StartNew();
            var hidDeviceList = list.GetHidDevices().ToArray();

            Console.WriteLine("Complete device list (took {0} ms to get {1} devices):",
                              stopwatch.ElapsedMilliseconds, hidDeviceList.Length);

            foreach (HidDevice dev in hidDeviceList)
            {
                if (dev.VendorID != 0x2341)
                {
                    continue;
                }

                Console.WriteLine(dev.DevicePath);
                //Console.WriteLine(string.Join(",", dev.GetDevicePathHierarchy())); // TODO
                Console.WriteLine(dev);

                try
                {
                    Console.WriteLine(string.Format("Max Lengths: Input {0}, Output {1}, Feature {2}",
                                                    dev.GetMaxInputReportLength(),
                                                    dev.GetMaxOutputReportLength(),
                                                    dev.GetMaxFeatureReportLength()));
                }
                catch (UnauthorizedAccessException e)
                {
                    Console.WriteLine(e);
                    Console.WriteLine();
                    continue;
                }

                try
                {
                    Console.WriteLine("Serial Ports: {0}", string.Join(",", dev.GetSerialPorts()));
                }
                catch
                {
                    Console.WriteLine("Serial Ports: Unknown on this platform.");
                }

                try
                {
                    var rawReportDescriptor = dev.GetRawReportDescriptor();
                    Console.WriteLine("Report Descriptor:");
                    Console.WriteLine("  {0} ({1} bytes)", string.Join(" ", rawReportDescriptor.Select(d => d.ToString("X2"))), rawReportDescriptor.Length);

                    int indent = 0;
                    foreach (var element in EncodedItem.DecodeItems(rawReportDescriptor, 0, rawReportDescriptor.Length))
                    {
                        if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.EndCollection)
                        {
                            indent -= 2;
                        }

                        Console.WriteLine("  {0}{1}", new string(' ', indent), element);

                        if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.Collection)
                        {
                            indent += 2;
                        }
                    }

                    var reportDescriptor = dev.GetReportDescriptor();

                    // Lengths should match.
                    Debug.Assert(dev.GetMaxInputReportLength() == reportDescriptor.MaxInputReportLength);
                    Debug.Assert(dev.GetMaxOutputReportLength() == reportDescriptor.MaxOutputReportLength);
                    Debug.Assert(dev.GetMaxFeatureReportLength() == reportDescriptor.MaxFeatureReportLength);

                    foreach (var deviceItem in reportDescriptor.DeviceItems)
                    {
                        foreach (var usage in deviceItem.Usages.GetAllValues())
                        {
                            Console.WriteLine(string.Format("Usage: {0:X4} {1}", usage, (Usage)usage));
                        }
                        foreach (var report in deviceItem.Reports)
                        {
                            Console.WriteLine(string.Format("{0}: ReportID={1}, Length={2}, Items={3}",
                                                            report.ReportType, report.ReportID, report.Length, report.DataItems.Count));
                            foreach (var dataItem in report.DataItems)
                            {
                                Console.WriteLine(string.Format("  {0} Elements x {1} Bits, Units: {2}, Expected Usage Type: {3}, Flags: {4}, Usages: {5}",
                                                                dataItem.ElementCount, dataItem.ElementBits, dataItem.Unit.System, dataItem.ExpectedUsageType, dataItem.Flags,
                                                                string.Join(", ", dataItem.Usages.GetAllValues().Select(usage => usage.ToString("X4") + " " + ((Usage)usage).ToString()))));
                            }
                        }

                        {
                            Console.WriteLine("Opening device for 20 seconds...");

                            HidStream hidStream;
                            if (dev.TryOpen(out hidStream))
                            {
                                hidStream.ReadTimeout = Timeout.Infinite;

                                using (hidStream)
                                {
                                    var inputReportBuffer = new byte[dev.GetMaxInputReportLength()];
                                    var inputReceiver     = reportDescriptor.CreateHidDeviceInputReceiver();
                                    var inputParser       = deviceItem.CreateDeviceItemInputParser();


                                    inputReceiver.Received += (sender, e) =>
                                    {
                                        Report report;
                                        while (inputReceiver.TryRead(inputReportBuffer, 0, out report))
                                        {
                                            // Parse the report if possible.
                                            // This will return false if (for example) the report applies to a different DeviceItem.
                                            if (inputParser.TryParseReport(inputReportBuffer, 0, report))
                                            {
                                                // If you are using Windows Forms, you could call BeginInvoke here to marshal the results
                                                // to your main thread.
                                                WriteDeviceItemInputParserResult(inputParser);
                                            }
                                        }
                                    };
                                    inputReceiver.Start(hidStream);

                                    Thread.Sleep(60000);
                                }

                                Console.WriteLine("Closed device.");
                            }
                            else
                            {
                                Console.WriteLine("Failed to open device.");
                            }

                            Console.WriteLine();
                        }
                    }

                    Console.WriteLine();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }

            Console.WriteLine("Press a key to exit...");
            Console.Read();
        }
        /// <summary>
        /// Parses a single <see cref="EncodedItem"/>.
        /// Call this repeatedly for every item to completely decode a report descriptor.
        /// </summary>
        /// <param name="item">The item to parse.</param>
        public void Parse(EncodedItem item)
        {
            if (item == null) { throw new ArgumentNullException("item"); }
            uint value = item.DataValue;

            switch (item.Type)
            {
                case ItemType.Main:
                    ParseMain(item.TagForMain, value);
                    LocalItemState.Clear();
                    break;

                case ItemType.Local:
                    switch (item.TagForLocal)
                    {
                        case LocalItemTag.Usage:
                        case LocalItemTag.UsageMinimum:
                        case LocalItemTag.UsageMaximum:
                            if (value <= 0xffff) { value |= GetGlobalItemValue(GlobalItemTag.UsagePage) << 16; }
                            break;
                    }
                    LocalItemState.Add(new KeyValuePair<LocalItemTag, uint>(item.TagForLocal, value));
                    break;

                case ItemType.Global:
                    switch (item.TagForGlobal)
                    {
                        case GlobalItemTag.Push:
                            GlobalItemStateStack.Add(new Dictionary<GlobalItemTag, EncodedItem>(GlobalItemState));
                            break;

                        case GlobalItemTag.Pop:
                            GlobalItemStateStack.RemoveAt(GlobalItemState.Count - 1);
                            break;

                        default:
                            switch (item.TagForGlobal)
                            {
                                case GlobalItemTag.ReportID:
                                    ReportsUseID = true; break;
                            }

                            GlobalItemState[item.TagForGlobal] = item;
                            break;
                    }
                    break;
            }
        }
        private void PrintfHidDeviceInfo()
        {
            HidDevice[] devices = DeviceList.Local.GetHidDevices(1111, 4755).ToArray();

            foreach (HidDevice dev in devices)
            {
                Console.WriteLine("-----------------------------");
                Console.WriteLine("{0}, {1}", dev, dev.DevicePath);

                try
                {
                    Console.WriteLine(string.Format("Max Lengths: Input {0}, Output {1}, Feature {2}", dev.GetMaxInputReportLength(), dev.GetMaxOutputReportLength(), dev.GetMaxFeatureReportLength()));
                }
                catch (UnauthorizedAccessException e)
                {
                    Console.WriteLine(e);
                    continue;
                }

                try
                {
                    var rawReportDescriptor = dev.GetRawReportDescriptor();
                    Console.WriteLine("Report Descriptor:");
                    Console.WriteLine("  {0} ({1} bytes)", string.Join(" ", rawReportDescriptor.Select(d => d.ToString("X2"))), rawReportDescriptor.Length);
                    int indent = 0;
                    foreach (EncodedItem element in EncodedItem.DecodeItems(rawReportDescriptor, 0, rawReportDescriptor.Length))
                    {
                        if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.EndCollection)
                        {
                            indent -= 4;
                        }
                        Console.WriteLine("  {0}{1}", new string(' ', indent), element);
                        if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.Collection)
                        {
                            indent += 4;
                        }
                    }

                    var reportDescriptor = dev.GetReportDescriptor();
                    foreach (DeviceItem deviceItem in reportDescriptor.DeviceItems)
                    {
                        if (InputParser == null)
                        {
                            InputParser = deviceItem.CreateDeviceItemInputParser();
                        }

                        foreach (var usage in deviceItem.Usages.GetAllValues())
                        {
                            Console.WriteLine(string.Format("Usage: {0:X4} {1}", usage, (Usage)usage));
                        }

                        foreach (var report in deviceItem.Reports)
                        {
                            Console.WriteLine(string.Format("{0}: ReportID={1}, Length={2}, Items={3}",
                                                            report.ReportType, report.ReportID, report.Length, report.DataItems.Count));
                            foreach (DataItem dataItem in report.DataItems)
                            {
                                Console.WriteLine(string.Format("  {0} Elements x {1} Bits, Units: {2}, Expected Usage Type: {3}, Flags: {4}, Usages: {5}  TotalBits: {6}",
                                                                dataItem.ElementCount, dataItem.ElementBits, dataItem.Unit.System, dataItem.ExpectedUsageType, dataItem.Flags,
                                                                string.Join(", ", dataItem.Usages.GetAllValues().Select(usage => usage.ToString("X4") + " " + ((Usage)usage).ToString())), dataItem.TotalBits));
                            }
                        }

                        //tryOpen
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Exception:::{0}", ex);
                }
            }
        }
示例#14
0
        static void Main(string[] args)
        {
            //Trace.Listeners.Clear();
            //Trace.Listeners.Add(new ConsoleTraceListener());

            HidSharpDiagnostics.EnableTracing       = true;
            HidSharpDiagnostics.PerformStrictChecks = true;

            var list = DeviceList.Local;

            list.Changed += (sender, e) => Console.WriteLine("Device list changed.");

            //Console.WriteLine("Beginning discovery.");
            //using (list.BeginBleDiscovery())
            {
                var allDeviceList = list.GetAllDevices().ToArray();
                Console.WriteLine("All device list:");
                foreach (Device dev in allDeviceList)
                {
                    Console.WriteLine(dev.ToString() + " @ " + dev.DevicePath);

                    /*
                     * if (dev is HidDevice)
                     * {
                     *  foreach (var serialPort in
                     *      (((HidDevice)dev).GetSerialPorts()))
                     *  {
                     *      Console.WriteLine("    " + serialPort);
                     *  }
                     * }
                     */
                }

                var bleDeviceList = list.GetBleDevices().ToArray();
                Console.WriteLine("BLE device list:");
                foreach (BleDevice dev in bleDeviceList)
                {
                    Console.WriteLine(dev.ToString() + "@" + dev.DevicePath);
                    foreach (var service in dev.GetServices())
                    {
                        Console.WriteLine(string.Format("\tService: {0}", service.Uuid));
                        foreach (var characteristic in service.GetCharacteristics())
                        {
                            Console.WriteLine(string.Format("\t\tCharacteristic: {0} (Properties: {1})", characteristic.Uuid, characteristic.Properties));
                            foreach (var descriptor in characteristic.GetDescriptors())
                            {
                                Console.WriteLine(string.Format("\t\t\tDescriptor: {0}", descriptor.Uuid));
                            }
                        }

                        if (service.Uuid == new BleUuid("63dc0001-fa35-4205-b09f-0fc6072ec515"))
                        {
                            try
                            {
                                using (var svc = dev.Open(service))
                                {
                                    Console.WriteLine("Opened!");

                                    BleCharacteristic rx = null;

                                    foreach (var ch in service.GetCharacteristics())
                                    {
                                        Console.WriteLine(string.Format("{0} = {1}", ch.Uuid, ch.IsReadable ? string.Join(" ", svc.ReadCharacteristic(ch)) : "N/A"));

                                        foreach (var d in ch.GetDescriptors())
                                        {
                                            Console.WriteLine(string.Format("\t{0} = {1}", d.Uuid, string.Join(" ", svc.ReadDescriptor(d))));
                                        }

                                        if (BleCccd.Notification != svc.ReadCccd(ch))
                                        {
                                            svc.WriteCccd(ch, BleCccd.Notification);
                                        }

                                        if (ch.Uuid == new BleUuid("63dc0002-fa35-4205-b09f-0fc6072ec515"))
                                        {
                                            rx = ch;
                                        }
                                    }

                                    Action        beginReadEvent = null;
                                    AsyncCallback endReadEvent   = null;
                                    beginReadEvent = () =>
                                    {
                                        svc.BeginReadEvent(endReadEvent, null);
                                    };
                                    endReadEvent = ar =>
                                    {
                                        BleEvent @event;

                                        try
                                        {
                                            @event = svc.EndReadEvent(ar);
                                        }
                                        catch (ObjectDisposedException)
                                        {
                                            Console.WriteLine("closed");
                                            return;
                                        }
                                        catch (TimeoutException)
                                        {
                                            Console.WriteLine("timed out");
                                            @event = default(BleEvent);
                                        }

                                        if (@event.Value != null)
                                        {
                                            Console.WriteLine(string.Format("{0} -> {1}", @event.Characteristic, string.Join(" ", @event.Value.Select(x => x.ToString()))));

                                            if (rx != null)
                                            {
                                                Console.WriteLine("writing");
                                                svc.WriteCharacteristicWithoutResponse(rx, new[] { (byte)0xdd, (byte)1, (byte)'A' });
                                            }
                                        }
                                        beginReadEvent();
                                    };
                                    beginReadEvent();

                                    Thread.Sleep(30000);
                                }
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.ToString());
                            }
                        }
                    }
                }

                Console.WriteLine();
                Console.WriteLine("Press any key");
                Console.ReadKey();
                Console.WriteLine();
            }
            //Console.WriteLine("Ending discovery.");

            /*
             * var serialDeviceList = list.GetSerialDevices().ToArray();
             * Console.WriteLine("Serial device list:");
             * foreach (SerialDevice dev in serialDeviceList)
             * {
             *  Console.WriteLine(dev.DevicePath);
             * }
             *
             * Console.WriteLine();
             */

            var stopwatch     = Stopwatch.StartNew();
            var hidDeviceList = list.GetHidDevices().ToArray();

            Console.WriteLine("Complete device list (took {0} ms to get {1} devices):",
                              stopwatch.ElapsedMilliseconds, hidDeviceList.Length);
            foreach (HidDevice dev in hidDeviceList)
            {
                Console.WriteLine(dev.DevicePath);
                //Console.WriteLine(string.Join(",", dev.GetDevicePathHierarchy())); // TODO
                Console.WriteLine(dev);

                try
                {
                    Console.WriteLine(string.Format("Max Lengths: Input {0}, Output {1}, Feature {2}",
                                                    dev.GetMaxInputReportLength(),
                                                    dev.GetMaxOutputReportLength(),
                                                    dev.GetMaxFeatureReportLength()));
                }
                catch (UnauthorizedAccessException e)
                {
                    Console.WriteLine(e);
                    Console.WriteLine();
                    continue;
                }

                try
                {
                    Console.WriteLine("Serial Ports: {0}", string.Join(",", dev.GetSerialPorts()));
                }
                catch
                {
                    Console.WriteLine("Serial Ports: Unknown on this platform.");
                }

                try
                {
                    var rawReportDescriptor = dev.GetRawReportDescriptor();
                    Console.WriteLine("Report Descriptor:");
                    Console.WriteLine("  {0} ({1} bytes)", string.Join(" ", rawReportDescriptor.Select(d => d.ToString("X2"))), rawReportDescriptor.Length);

                    int indent = 0;
                    foreach (var element in EncodedItem.DecodeItems(rawReportDescriptor, 0, rawReportDescriptor.Length))
                    {
                        if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.EndCollection)
                        {
                            indent -= 2;
                        }

                        Console.WriteLine("  {0}{1}", new string(' ', indent), element);

                        if (element.ItemType == ItemType.Main && element.TagForMain == MainItemTag.Collection)
                        {
                            indent += 2;
                        }
                    }

                    var reportDescriptor = dev.GetReportDescriptor();

                    // Lengths should match.
                    Debug.Assert(dev.GetMaxInputReportLength() == reportDescriptor.MaxInputReportLength);
                    Debug.Assert(dev.GetMaxOutputReportLength() == reportDescriptor.MaxOutputReportLength);
                    Debug.Assert(dev.GetMaxFeatureReportLength() == reportDescriptor.MaxFeatureReportLength);

                    foreach (var deviceItem in reportDescriptor.DeviceItems)
                    {
                        foreach (var usage in deviceItem.Usages.GetAllValues())
                        {
                            Console.WriteLine(string.Format("Usage: {0:X4} {1}", usage, (Usage)usage));
                        }
                        foreach (var report in deviceItem.Reports)
                        {
                            Console.WriteLine(string.Format("{0}: ReportID={1}, Length={2}, Items={3}",
                                                            report.ReportType, report.ReportID, report.Length, report.DataItems.Count));
                            foreach (var dataItem in report.DataItems)
                            {
                                Console.WriteLine(string.Format("  {0} Elements x {1} Bits, Units: {2}, Expected Usage Type: {3}, Flags: {4}, Usages: {5}",
                                                                dataItem.ElementCount, dataItem.ElementBits, dataItem.Unit.System, dataItem.ExpectedUsageType, dataItem.Flags,
                                                                string.Join(", ", dataItem.Usages.GetAllValues().Select(usage => usage.ToString("X4") + " " + ((Usage)usage).ToString()))));
                            }
                        }

                        {
                            Console.WriteLine("Opening device for 20 seconds...");

                            HidStream hidStream;
                            if (dev.TryOpen(out hidStream))
                            {
                                Console.WriteLine("Opened device.");
                                hidStream.ReadTimeout = Timeout.Infinite;

                                using (hidStream)
                                {
                                    var inputReportBuffer = new byte[dev.GetMaxInputReportLength()];
                                    var inputReceiver     = reportDescriptor.CreateHidDeviceInputReceiver();
                                    var inputParser       = deviceItem.CreateDeviceItemInputParser();

#if SINGLE_THREADED_WAITHANDLE_APPROACH
                                    inputReceiver.Start(hidStream);

                                    int startTime = Environment.TickCount;
                                    while (true)
                                    {
                                        if (inputReceiver.WaitHandle.WaitOne(1000))
                                        {
                                            if (!inputReceiver.IsRunning)
                                            {
                                                break;
                                            }                                        // Disconnected?

                                            Report report;
                                            while (inputReceiver.TryRead(inputReportBuffer, 0, out report))
                                            {
                                                // Parse the report if possible.
                                                // This will return false if (for example) the report applies to a different DeviceItem.
                                                if (inputParser.TryParseReport(inputReportBuffer, 0, report))
                                                {
                                                    WriteDeviceItemInputParserResult(inputParser);
                                                }
                                            }
                                        }

                                        uint elapsedTime = (uint)(Environment.TickCount - startTime);
                                        if (elapsedTime >= 20000)
                                        {
                                            break;
                                        }                                    // Stay open for 20 seconds.
                                    }
#elif SINGLE_THREADED_POLLING_APPROACH
                                    inputReceiver.Start(hidStream);

                                    int startTime = Environment.TickCount;
                                    while (true)
                                    {
                                        if (!inputReceiver.IsRunning)
                                        {
                                            break;
                                        }              // Disconnected?

                                        Report report; // Periodically check if the receiver has any reports.
                                        while (inputReceiver.TryRead(inputReportBuffer, 0, out report))
                                        {
                                            // Parse the report if possible.
                                            // This will return false if (for example) the report applies to a different DeviceItem.
                                            if (inputParser.TryParseReport(inputReportBuffer, 0, report))
                                            {
                                                WriteDeviceItemInputParserResult(inputParser);
                                            }
                                        }
                                    }

                                    uint elapsedTime = (uint)(Environment.TickCount - startTime);
                                    if (elapsedTime >= 20000)
                                    {
                                        break;
                                    }                                    // Stay open for 20 seconds.
#elif THREAD_POOL_RECEIVED_EVENT_APPROACH
                                    inputReceiver.Received += (sender, e) =>
                                    {
                                        Report report;
                                        while (inputReceiver.TryRead(inputReportBuffer, 0, out report))
                                        {
                                            // Parse the report if possible.
                                            // This will return false if (for example) the report applies to a different DeviceItem.
                                            if (inputParser.TryParseReport(inputReportBuffer, 0, report))
                                            {
                                                // If you are using Windows Forms, you could call BeginInvoke here to marshal the results
                                                // to your main thread.
                                                WriteDeviceItemInputParserResult(inputParser);
                                            }
                                        }
                                    };
                                    inputReceiver.Start(hidStream);

                                    Thread.Sleep(20000);
#elif RAW_APPROACH
                                    IAsyncResult ar = null;

                                    int startTime = Environment.TickCount;
                                    while (true)
                                    {
                                        if (ar == null)
                                        {
                                            ar = hidStream.BeginRead(inputReportBuffer, 0, inputReportBuffer.Length, null, null);
                                        }

                                        if (ar != null)
                                        {
                                            if (ar.IsCompleted)
                                            {
                                                int byteCount = hidStream.EndRead(ar);
                                                ar = null;

                                                if (byteCount > 0)
                                                {
                                                    string hexOfBytes = string.Join(" ", inputReportBuffer.Take(byteCount).Select(b => b.ToString("X2")));
                                                    Console.WriteLine("  {0}", hexOfBytes);
                                                }
                                            }
                                            else
                                            {
                                                ar.AsyncWaitHandle.WaitOne(1000);
                                            }
                                        }

                                        uint elapsedTime = (uint)(Environment.TickCount - startTime);
                                        if (elapsedTime >= 20000)
                                        {
                                            break;
                                        }                                    // Stay open for 20 seconds.
                                    }
#else
#error "Choose an approach for the example."
#endif
                                }

                                Console.WriteLine("Closed device.");
                            }
                            else
                            {
                                Console.WriteLine("Failed to open device.");
                            }

                            Console.WriteLine();
                        }
                    }

                    Console.WriteLine();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }

            Console.WriteLine("Press a key to exit...");
            Console.ReadKey();
        }