Exemplo n.º 1
0
        public void WriteCccd(BleCharacteristic characteristic, BleCccd cccd, BleRequestFlags requestFlags)
        {
            Throw.If.Null(characteristic, "characteristic");

            BleDescriptor descriptor;

            if (!characteristic.TryGetDescriptor(BleUuids.Cccd, out descriptor))
            {
                HidSharpDiagnostics.Trace("Characteristic {0} does not have a CCCD, so {1} could not be written.", characteristic, cccd);
                return;
            }

            if (cccd == BleCccd.Notification)
            {
                HidSharpDiagnostics.PerformStrictCheck(characteristic.IsNotifiable, "Characteristic doesn't support Notify.");
            }

            if (cccd == BleCccd.Indication)
            {
                HidSharpDiagnostics.PerformStrictCheck(characteristic.IsIndicatable, "Characteristic doesn't support Indicate.");
            }


            var value = new byte[2];

            value[0] = (byte)((ushort)cccd >> 0);
            value[1] = (byte)((ushort)cccd >> 8);
            WriteDescriptor(descriptor, value);
        }
Exemplo n.º 2
0
        public override void WriteCharacteristicWithoutResponse(BleCharacteristic characteristic, byte[] value, int offset, int count, BleRequestFlags requestFlags)
        {
            Throw.If.Null(characteristic, "characteristic");
            HidSharpDiagnostics.PerformStrictCheck(characteristic.IsWritableWithoutResponse, "Characteristic doesn't support Write Without Response.");

            var flags = GetGattFlags(requestFlags);

            WriteCharacteristic(characteristic, value, offset, count, flags | NativeMethods.BLUETOOTH_GATT_FLAGS.WRITE_WITHOUT_RESPONSE);
        }
Exemplo n.º 3
0
        public override void WriteCharacteristic(BleCharacteristic characteristic, byte[] value, int offset, int count, BleRequestFlags requestFlags)
        {
            Throw.If.Null(characteristic, "characteristic");
            HidSharpDiagnostics.PerformStrictCheck(characteristic.IsWritable, "Characteristic doesn't support Write.");

            var flags = GetGattFlags(requestFlags);

            WriteCharacteristic(characteristic, value, offset, count, flags);
        }
Exemplo n.º 4
0
        public override unsafe byte[] ReadCharacteristic(BleCharacteristic characteristic, BleRequestFlags requestFlags)
        {
            Throw.If.Null(characteristic, "characteristic");
            HidSharpDiagnostics.PerformStrictCheck(characteristic.IsReadable, "Characteristic doesn't support Read.");

            var flags = GetGattFlags(requestFlags);

            HandleAcquireIfOpenOrFail();
            try
            {
                lock (_readSync)
                {
                    int error;
                    var wc = (WinBleCharacteristic)characteristic;

                    ushort valueSize;
                    error = NativeMethods.BluetoothGATTGetCharacteristicValue(_handle,
                                                                              ref wc.NativeData,
                                                                              0, null,
                                                                              out valueSize,
                                                                              flags | ((requestFlags & BleRequestFlags.Cacheable) == 0 ? NativeMethods.BLUETOOTH_GATT_FLAGS.FORCE_READ_FROM_DEVICE : 0));
                    if (error != NativeMethods.ERROR_MORE_DATA || valueSize < NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE.Size)
                    {
                        var message = string.Format("Failed to read characteristic {0}.", characteristic);
                        throw DeviceException.CreateIOException(Device, message, error);
                    }

                    var cb = stackalloc byte[valueSize];
                    var cv = (NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE *)cb;

                    ushort valueSize2;
                    error = NativeMethods.BluetoothGATTGetCharacteristicValue(_handle,
                                                                              ref wc.NativeData,
                                                                              valueSize,
                                                                              cv,
                                                                              out valueSize2,
                                                                              flags);
                    if (error != 0 || valueSize != valueSize2 || cv->DataSize > valueSize - NativeMethods.BTH_LE_GATT_CHARACTERISTIC_VALUE.Size)
                    {
                        var message = string.Format("Failed to read characteristic {0}.", characteristic);
                        throw DeviceException.CreateIOException(Device, message, error);
                    }

                    var bytes = new byte[cv->DataSize];
                    Marshal.Copy((IntPtr)(void *)&cv->Data[0], bytes, 0, checked ((int)cv->DataSize));
                    return(bytes);
                }
            }
            finally
            {
                HandleRelease();
            }
        }
Exemplo n.º 5
0
        internal void Init(NativeMethods.io_string_t path)
        {
            IntPtr handle; int retryCount = 0, maxRetries = 10;

            while (true)
            {
                var newPath = path.Clone();
                using (var service = NativeMethods.IORegistryEntryFromPath(0, ref newPath).ToIOObject())
                {
                    string error;

                    if (service.IsSet)
                    {
                        handle = NativeMethods.IOHIDDeviceCreate(IntPtr.Zero, service);
                        if (handle != IntPtr.Zero)
                        {
                            var ret = NativeMethods.IOHIDDeviceOpen(handle);
                            if (ret == NativeMethods.IOReturn.Success)
                            {
                                break;
                            }

                            NativeMethods.CFRelease(handle);

                            // TODO: Only count up if IOReturn is ExclusiveAccess or Offline.
                            error = string.Format("Unable to open HID class device (error {1}): {0}", newPath.ToString(), ret);
                        }
                        else
                        {
                            error = string.Format("HID class device not found: {0}", newPath.ToString());
                        }
                    }
                    else
                    {
                        error = string.Format("HID class device path not found: {0}", newPath.ToString());
                    }

                    if (++retryCount == maxRetries)
                    {
                        throw DeviceException.CreateIOException(Device, error);
                    }

                    HidSharpDiagnostics.Trace("Retrying ({0})", error);
                    Thread.Sleep(100);
                }
            }
            _handle = handle;
            HandleInitAndOpen();

            _readThread.Start();
            _writeThread.Start();
        }
Exemplo n.º 6
0
        /// <inheritdoc/>
        protected override void Dispose(bool disposing)
        {
            try
            {
                OnClosed();
            }
            catch (Exception e)
            {
                HidSharpDiagnostics.Trace("OnClosed threw an exception: {0}", e);
            }

            base.Dispose(disposing);
        }
Exemplo n.º 7
0
        public DeviceOpenUtility(Device device, string streamPath, OpenConfiguration openConfig)
        {
            _device = device;

            _syncRoot       = new object();
            _resourcePrefix = GetResourcePrefix(streamPath);

            _priority               = (OpenPriority)openConfig.GetOption(OpenOption.Priority);
            _interruptible          = (bool)openConfig.GetOption(OpenOption.Interruptible);
            _transient              = (bool)openConfig.GetOption(OpenOption.Transient);
            _timeoutIfInterruptible = (int)openConfig.GetOption(OpenOption.TimeoutIfInterruptible);
            _timeoutIfTransient     = (int)openConfig.GetOption(OpenOption.TimeoutIfTransient);

            HidSharpDiagnostics.Trace("Opening a device. Our priority is {0}, our interruptible state is {1}, and our transient state is {2}.",
                                      _priority, _interruptible, _transient);
        }
Exemplo n.º 8
0
        // usbser.sys does not register an interface, at least on Windows 7 and earlier. (Other serial drivers don't suffer from this flaw.)
        // In any case, to detect connections and disconnections, let's watch the SERIALCOMM registry key.
        static unsafe void SerialWatcherThread()
        {
            var notifyEvent = NativeMethods.CreateAutoResetEventOrThrow();

            try
            {
                IntPtr handle;
                if (0 == NativeMethods.RegOpenKeyEx(new IntPtr(unchecked ((int)NativeMethods.HKEY_LOCAL_MACHINE)), @"HARDWARE\DEVICEMAP\SERIALCOMM", 0, NativeMethods.KEY_NOTIFY, out handle))
                {
                    try
                    {
                        var handles = stackalloc IntPtr[2];
                        handles[0] = _serialWatcherShutdownEvent;
                        handles[1] = notifyEvent;

                        while (true)
                        {
                            if (0 != NativeMethods.RegNotifyChangeKeyValue(handle, false, NativeMethods.REG_NOTIFY_CHANGE_LAST_SET, notifyEvent, true))
                            {
                                break;
                            }

                            switch (NativeMethods.WaitForMultipleObjects(2, handles, false, uint.MaxValue))
                            {
                            case NativeMethods.WAIT_OBJECT_0:
                            default:
                                return;

                            case NativeMethods.WAIT_OBJECT_1:
                                HidSharpDiagnostics.Trace("Received a serial change event.");
                                DeviceListDidChange(ref _serNotifyObject); break;
                            }
                        }
                    }
                    finally
                    {
                        NativeMethods.RegCloseKey(handle);
                    }
                }
            }
            finally
            {
                NativeMethods.CloseHandle(notifyEvent);
            }
        }
Exemplo n.º 9
0
        protected virtual DeviceStream OpenDeviceAndRestrictAccess(OpenConfiguration openConfig)
        {
            bool exclusive = (bool)openConfig.GetOption(OpenOption.Exclusive);

            DeviceOpenUtility openUtility = null;

            if (exclusive)
            {
                string streamPath = GetStreamPath(openConfig);
                openUtility = new DeviceOpenUtility(this, streamPath, openConfig);
                openUtility.Open();
            }

            DeviceStream stream;

            try
            {
                stream = OpenDeviceDirectly(openConfig);
                if (exclusive)
                {
                    stream.Closed += (sender, e) => openUtility.Close();
                    openUtility.InterruptRequested += (sender, e) =>
                    {
                        stream.OnInterruptRequested();
                        HidSharpDiagnostics.Trace("Delivered an interrupt request.");
                    };
                }
            }
            catch
            {
                if (exclusive)
                {
                    openUtility.Close();
                }
                throw;
            }

            return(stream);
        }
Exemplo n.º 10
0
        public BleCccd ReadCccd(BleCharacteristic characteristic, BleRequestFlags requestFlags)
        {
            BleDescriptor descriptor;

            if (!characteristic.TryGetDescriptor(BleUuids.Cccd, out descriptor))
            {
                HidSharpDiagnostics.Trace("Characteristic {0} does not have a CCCD, so it could not be read.", characteristic);
                return(BleCccd.None);
            }

            var value = ReadDescriptor(descriptor, requestFlags); var cccd = BleCccd.None;

            if (value.Length >= 1 && value[0] == (byte)BleCccd.Notification)
            {
                cccd = BleCccd.Notification;
            }
            if (value.Length >= 1 && value[0] == (byte)BleCccd.Indication)
            {
                cccd = BleCccd.Indication;
            }
            return(cccd);
        }
Exemplo n.º 11
0
        void Run()
        {
            SystemEvent exclusiveEvent = null;
            SystemMutex exclusiveMutex = null; IDisposable exclusiveLock = null;
            SystemMutex priorityMutex  = null;
            SystemMutex transientMutex = null;

            try
            {
                var em = HidSelector.Instance.EventManager;

                // *** Open the device.
                try
                {
                    string transientName = GetResourceName("Transient");

                    // Create or open the exclusive event.
                    exclusiveEvent = em.CreateEvent(GetResourceName("Event"));

                    // Try to acquire the device.
                    exclusiveMutex = em.CreateMutex(GetResourceName("Lock"));
                    if (!exclusiveMutex.TryLock(0, out exclusiveLock))
                    {
                        // We failed just locking it outright. First, can we interrupt?
                        bool lockIsInterruptible = false;
                        for (int priority = (int)OpenPriority.Idle; priority < (int)_priority; priority++)
                        {
                            if (em.MutexMayExist(GetResourceNameForPriority((OpenPriority)priority)))
                            {
                                lockIsInterruptible = true; break;
                            }
                        }

                        // Let's try again.
                        bool lockIsTransient = em.MutexMayExist(transientName);
                        using (var tryPriorityMutex = (lockIsInterruptible ? em.CreateMutex(GetResourceNameForPriorityRequest()) : null))
                        {
                            exclusiveEvent.Set();

                            int timeout;
                            if (lockIsTransient)
                            {
                                timeout = Math.Max(0, _timeoutIfTransient);
                                HidSharpDiagnostics.Trace("Failed to open the device. Luckily, it is in use by a transient process. We will wait {0} ms.", timeout);
                            }
                            else if (lockIsInterruptible)
                            {
                                timeout = Math.Max(0, _timeoutIfInterruptible);
                                HidSharpDiagnostics.Trace("Failed to open the device. Luckily, it is in use by an interruptible process. We will wait {0} ms.", timeout);
                            }
                            else
                            {
                                timeout = 0;
                            }

                            if (!exclusiveMutex.TryLock(timeout, out exclusiveLock))
                            {
                                throw DeviceException.CreateIOException(_device, "The device is in use.", Utility.HResult.SharingViolation);
                            }
                        }
                    }

                    if (_transient)
                    {
                        transientMutex = em.CreateMutex(transientName);
                    }

                    if (_interruptible)
                    {
                        priorityMutex = em.CreateMutex(GetResourceNameForPriority(_priority));
                    }
                }
                catch (Exception e)
                {
                    _threadStartError = e; return;
                }
                finally
                {
                    _threadStartEvent.Set();
                }

                // *** OK! Now run the sharing monitor.
                {
                    var       handles = new WaitHandle[] { _closeEvent, exclusiveEvent.WaitHandle };
                    Exception ex      = null;

                    HidSharpDiagnostics.Trace("Started the sharing monitor thread ({0}).",
                                              Thread.CurrentThread.ManagedThreadId);
                    while (true)
                    {
                        try
                        {
                            if (WaitHandle.WaitAny(handles) == 0)
                            {
                                break;
                            }
                        }
                        catch (Exception e)
                        {
                            ex = e; break;
                        }

                        lock (_syncRoot)
                        {
                            // Okay. We received the request. Let's check for request priorities higher than ours.
                            exclusiveEvent.Reset();

                            HidSharpDiagnostics.Trace("Received an interrupt request ({0}).",
                                                      Thread.CurrentThread.ManagedThreadId);

                            if (em.MutexMayExist(GetResourceNameForPriorityRequest()))
                            {
                                ThreadPool.QueueUserWorkItem(_ =>
                                {
                                    var ev = InterruptRequested;
                                    if (ev != null)
                                    {
                                        ev(this, EventArgs.Empty);
                                    }
                                });
                            }
                        }
                    }

                    HidSharpDiagnostics.Trace("Exited its sharing monitor thread ({0}).{1}",
                                              Thread.CurrentThread.ManagedThreadId,
                                              (ex != null ? " " + ex.ToString() : ""));
                }
            }
            finally
            {
                Close(ref priorityMutex);
                Close(ref transientMutex);
                Close(ref exclusiveLock);
                Close(ref exclusiveMutex);
                Close(ref exclusiveEvent);

                _closeEvent.Close();
                _closeEvent = null;
                _thread     = null;
            }
        }
Exemplo n.º 12
0
        IEnumerable <Device> GetDevices(DeviceTypeInfo type)
        {
            var _deviceList             = type.DeviceList;
            var devicesLock             = type.DevicesLock;
            var getDeviceKeysCallback   = type.GetDeviceKeysCallback;
            var tryCreateDeviceCallback = type.TryCreateDeviceCallback;

            Device[] deviceListArray;

            lock (devicesLock)
            {
                object[] devices   = getDeviceKeysCallback();
                object[] additions = devices.Except(_deviceList.Keys).ToArray();
                object[] removals  = _deviceList.Keys.Except(devices).ToArray();

                if (additions.Length > 0)
                {
                    int completedAdditions = 0;

                    foreach (object addition in additions)
                    {
                        ThreadPool.QueueUserWorkItem(new WaitCallback(key =>
                        {
                            Device device;
                            bool created = tryCreateDeviceCallback(key, out device);

                            if (created)
                            {
                                // By not adding on failure, we'll end up retrying every time.
                                lock (_deviceList)
                                {
                                    _deviceList.Add(key, device);
                                    HidSharpDiagnostics.Trace("Detected a new device: {0}", key);
                                }
                            }

                            lock (_deviceList)
                            {
                                completedAdditions++; Monitor.Pulse(_deviceList);
                            }
                        }), addition);
                    }

                    lock (_deviceList)
                    {
                        while (completedAdditions != additions.Length)
                        {
                            Monitor.Wait(_deviceList);
                        }
                    }
                }

                foreach (object key in removals)
                {
                    _deviceList.Remove(key);
                    HidSharpDiagnostics.Trace("Detected a device removal: {0}", key);
                }
                deviceListArray = _deviceList.Values.ToArray();
            }

            return(deviceListArray);
        }
Exemplo n.º 13
0
        unsafe static IntPtr DeviceMonitorWindowProc(IntPtr window, uint message, IntPtr wParam, IntPtr lParam)
        {
            if (message == NativeMethods.WM_DEVICECHANGE)
            {
                var ev = (NativeMethods.WM_DEVICECHANGE_wParam)(int)(long) wParam;
                HidSharpDiagnostics.Trace("Received a device change event, {0}.", ev);

                var eventArgs = (NativeMethods.DEV_BROADCAST_HDR *)(void *) lParam;

                if (ev == NativeMethods.WM_DEVICECHANGE_wParam.DBT_DEVICEARRIVAL || ev == NativeMethods.WM_DEVICECHANGE_wParam.DBT_DEVICEREMOVECOMPLETE)
                {
                    if (eventArgs->DeviceType == NativeMethods.DBT_DEVTYP_DEVICEINTERFACE)
                    {
                        var diEventArgs = (NativeMethods.DEV_BROADCAST_DEVICEINTERFACE *)eventArgs;

                        if (diEventArgs->ClassGuid == NativeMethods.HidD_GetHidGuid())
                        {
                            DeviceListDidChange(ref _hidNotifyObject);
                        }
                        else if (diEventArgs->ClassGuid == NativeMethods.GuidForBluetoothLEDevice)
                        {
                            DeviceListDidChange(ref _bleNotifyObject);
                        }
                    }
                }
                else if (ev == NativeMethods.WM_DEVICECHANGE_wParam.DBT_CUSTOMEVENT)
                {
                    if (eventArgs->DeviceType == NativeMethods.DBT_DEVTYP_HANDLE)
                    {
                        var handleEventArgs = (NativeMethods.DEV_BROADCAST_HANDLE *)eventArgs;

                        if (handleEventArgs->EventGuid == NativeMethods.GuidForBluetoothHciEvent)
                        {
                            var hciEvent = (NativeMethods.BTH_HCI_EVENT_INFO *) & handleEventArgs->Data[0];
                            HidSharpDiagnostics.Trace("Bluetooth HCI event: address {0:X}, type {1}, connected {2}",
                                                      hciEvent->bthAddress, hciEvent->connectionType, hciEvent->connected);
                        }
                        else if (handleEventArgs->EventGuid == NativeMethods.GuidForBluetoothRadioInRange)
                        {
                            var radioInRange = (NativeMethods.BTH_RADIO_IN_RANGE *) & handleEventArgs->Data[0];
                            HidSharpDiagnostics.Trace("Radio in range event: address {0:X}, flags {1}, class {2}, name '{3}{4}'",
                                                      radioInRange->deviceInfo.address,
                                                      radioInRange->deviceInfo.flags,
                                                      radioInRange->deviceInfo.classOfDevice,
                                                      (char)radioInRange->deviceInfo.name[0],
                                                      (char)radioInRange->deviceInfo.name[1]);
                        }
                        else if (handleEventArgs->EventGuid == NativeMethods.GuidForBluetoothRadioOutOfRange)
                        {
                            var address = (NativeMethods.BLUETOOTH_ADDRESS *) & handleEventArgs->Data[0];
                            HidSharpDiagnostics.Trace("Radio out of range event: address {0:X}",
                                                      address->Addr);
                        }
                        else
                        {
                            HidSharpDiagnostics.Trace("Custom event: GUID {0}", handleEventArgs->EventGuid);
                        }

                        // For now, this doesn't raise the DeviceList Changed event. It only seems to occur at connection.
                    }
                }

                return((IntPtr)1);
            }

            return(NativeMethods.DefWindowProc(window, message, wParam, lParam));
        }
Exemplo n.º 14
0
        protected override void Run(Action readyCallback)
        {
            const string className = "HidSharpDeviceMonitor";

            NativeMethods.WindowProc windowProc = DeviceMonitorWindowProc;
            var wc = new NativeMethods.WNDCLASS()
            {
                ClassName = className, WindowProc = windowProc
            };

            RunAssert(0 != NativeMethods.RegisterClass(ref wc), "HidSharp RegisterClass failed.");

            var hwnd = NativeMethods.CreateWindowEx(0, className, className, 0,
                                                    NativeMethods.CW_USEDEFAULT, NativeMethods.CW_USEDEFAULT, NativeMethods.CW_USEDEFAULT, NativeMethods.CW_USEDEFAULT,
                                                    NativeMethods.HWND_MESSAGE,
                                                    IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

            RunAssert(hwnd != IntPtr.Zero, "HidSharp CreateWindow failed.");

            var hidNotifyHandle = RegisterDeviceNotification(hwnd, NativeMethods.HidD_GetHidGuid());
            var bleNotifyHandle = RegisterDeviceNotification(hwnd, NativeMethods.GuidForBluetoothLEDevice);

#if BLUETOOTH_NOTIFY
            var bleHandles = new List <BleRadio>(); // FIXME: We don't handle the removal of USB Bluetooth dongles here, as far as notifications go.

            IntPtr searchHandle, radioHandle;
            var    searchParams = new NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS()
            {
                Size = Marshal.SizeOf(typeof(NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS))
            };

            searchHandle = NativeMethods.BluetoothFindFirstRadio(ref searchParams, out radioHandle);
            if (searchHandle != IntPtr.Zero)
            {
                do
                {
                    var radio = new BleRadio();
                    radio.RadioHandle  = radioHandle;
                    radio.NotifyHandle = RegisterDeviceNotification(hwnd, radioHandle);
                    bleHandles.Add(radio);
                }while (NativeMethods.BluetoothFindNextRadio(searchHandle, out radioHandle));

                NativeMethods.BluetoothFindRadioClose(searchHandle);
            }

            if (bleHandles.Count > 0)
            {
                HidSharpDiagnostics.Trace("Found {0} Bluetooth radio(s).", bleHandles.Count);
            }
#endif

            //_bleDiscoveryThread = new Thread(BleDiscoveryThread) { IsBackground = true, Name = "HidSharp BLE Discovery" };
            //_bleDiscoveryThread.Start();

            _serialWatcherShutdownEvent = NativeMethods.CreateManualResetEventOrThrow();
            _serialWatcherThread        = new Thread(SerialWatcherThread)
            {
                IsBackground = true, Name = "HidSharp Serial Watcher"
            };
            _serialWatcherThread.Start();

            _hidNotifyObject = new object();
            _serNotifyObject = new object();
            _bleNotifyObject = new object();
            _notifyThread    = new Thread(DeviceMonitorEventThread)
            {
                IsBackground = true, Name = "HidSharp RaiseChanged"
            };
            _notifyThread.Start();

            readyCallback();

            NativeMethods.MSG msg;
            while (true)
            {
                int result = NativeMethods.GetMessage(out msg, hwnd, 0, 0);
                if (result == 0 || result == -1)
                {
                    break;
                }

                NativeMethods.TranslateMessage(ref msg);
                NativeMethods.DispatchMessage(ref msg);
            }

            //lock (_bleDiscoveryThread) { _bleDiscoveryShuttingDown = true; Monitor.Pulse(_bleDiscoveryThread); }
            lock (_notifyThread) { _notifyThreadShuttingDown = true; Monitor.Pulse(_notifyThread); }
            NativeMethods.SetEvent(_serialWatcherShutdownEvent);
            //_bleDiscoveryThread.Join();
            _notifyThread.Join();
            _serialWatcherThread.Join();

            UnregisterDeviceNotification(hidNotifyHandle);
            UnregisterDeviceNotification(bleNotifyHandle);
#if BLUETOOTH_NOTIFY
            foreach (var bleHandle in bleHandles)
            {
                UnregisterDeviceNotification(bleHandle.NotifyHandle); NativeMethods.CloseHandle(bleHandle.RadioHandle);
            }
#endif

            RunAssert(NativeMethods.DestroyWindow(hwnd), "HidSharp DestroyWindow failed.");
            RunAssert(NativeMethods.UnregisterClass(className, IntPtr.Zero), "HidSharp UnregisterClass failed.");
            GC.KeepAlive(windowProc);
        }