public virtual void Start() { ChildDevices.CollectionChanged += (sender, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: foreach (IDualShockDevice item in args.NewItems) { ChildDeviceAttached?.Invoke(this, new ChildDeviceAttachedEventArgs(item)); } break; case NotifyCollectionChangedAction.Remove: foreach (IDualShockDevice item in args.OldItems) { ChildDeviceRemoved?.Invoke(this, new ChildDeviceRemovedEventArgs(item)); } break; } }; _deviceLookupTask = _deviceLookupSchedule.Subscribe(OnLookup); OnLookup(0); }
private void OnLookup(long l) { if (!Monitor.TryEnter(_hostLookupTask)) { return; } try { var instanceId = 0; while (Devcon.Find(AirBenderHost.ClassGuid, out var path, out var instance, instanceId++)) { if (_hosts.Any(h => h.DevicePath.Equals(path))) { continue; } Log.Information("Found AirBender device {Path} ({Instance})", path, instance); var host = new AirBenderHost(path); host.HostDeviceDisconnected += (sender, args) => { var device = (AirBenderHost)sender; _hosts.Remove(device); device.Dispose(); }; host.ChildDeviceAttached += (o, eventArgs) => ChildDeviceAttached?.Invoke(this, new ChildDeviceAttachedEventArgs(eventArgs.Device)); host.ChildDeviceRemoved += (o, eventArgs) => ChildDeviceRemoved?.Invoke(this, new ChildDeviceRemovedEventArgs(eventArgs.Device)); host.InputReportReceived += (sender, args) => InputReportReceived?.Invoke(this, new InputReportReceivedEventArgs(args.Device, args.Report)); _hosts.Add(host); } } finally { Monitor.Exit(_hostLookupTask); } }
/// <summary> /// Opens an AirBender device by its device path. /// </summary> /// <param name="devicePath">The device path to open.</param> public AirBenderHost(string devicePath) { DevicePath = devicePath; Children = new ObservableCollection <AirBenderChildDevice>(); Children.CollectionChanged += (sender, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: foreach (IDualShockDevice item in args.NewItems) { ChildDeviceAttached?.Invoke(this, new ChildDeviceAttachedEventArgs(item)); } break; case NotifyCollectionChangedAction.Remove: foreach (IDualShockDevice item in args.OldItems) { ChildDeviceRemoved?.Invoke(this, new ChildDeviceRemovedEventArgs(item)); } break; default: break; } }; // // Open device // DeviceHandle = Kernel32.CreateFile(DevicePath, Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH | Kernel32.CreateFileFlags.FILE_FLAG_OVERLAPPED, Kernel32.SafeObjectHandle.Null ); if (DeviceHandle.IsInvalid) { throw new ArgumentException($"Couldn't open device {DevicePath}"); } var length = Marshal.SizeOf(typeof(AirbenderGetHostBdAddr)); var pData = Marshal.AllocHGlobal(length); bool ret; try { // // Request host MAC address // ret = DeviceHandle.OverlappedDeviceIoControl( IoctlAirbenderGetHostBdAddr, IntPtr.Zero, 0, pData, length, out _); if (!ret) { throw new AirBenderGetHostBdAddrFailedException( "Failed to request host MAC address.", new Win32Exception(Marshal.GetLastWin32Error())); } HostAddress = new PhysicalAddress(Marshal.PtrToStructure <AirbenderGetHostBdAddr>(pData).Host.Address.Reverse() .ToArray()); } finally { Marshal.FreeHGlobal(pData); } Log.Information("Bluetooth Host Address: {HostAddress}", HostAddress.AsFriendlyName()); // // Request host controller to reset and clean up resources // ret = DeviceHandle.OverlappedDeviceIoControl( IoctlAirbenderHostReset, IntPtr.Zero, 0, IntPtr.Zero, 0, out _); if (!ret) { throw new AirBenderHostResetFailedException( "Failed to reset host.", new Win32Exception(Marshal.GetLastWin32Error())); } Task.Factory.StartNew(ChildDeviceArrivalWorker, _arrivalCancellationTokenSourcePrimary.Token); Task.Factory.StartNew(ChildDeviceArrivalWorker, _arrivalCancellationTokenSourceSecondary.Token); Task.Factory.StartNew(ChildDeviceRemovalWorker, _removalCancellationTokenSourcePrimary.Token); Task.Factory.StartNew(ChildDeviceRemovalWorker, _removalCancellationTokenSourceSecondary.Token); }