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); }
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(); }
/// <inheritdoc/> protected override void Dispose(bool disposing) { try { OnClosed(); } catch (Exception e) { HidSharpDiagnostics.Trace("OnClosed threw an exception: {0}", e); } base.Dispose(disposing); }
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); }
// 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); } }
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); }
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); }
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; } }
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); }
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)); }
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); }