ScpControl/Usb/UsbHub.cs (242 lines of code) (raw):

using System; using System.ComponentModel; using System.Linq; using ScpControl.ScpCore; using ScpControl.Shared.Core; using ScpControl.Sound; using ScpControl.Usb.Ds3; using ScpControl.Usb.Ds4; using ScpControl.Usb.Gamepads; using ScpControl.Utilities; namespace ScpControl.Usb { /// <summary> /// Represents an Usb hub. /// </summary> public partial class UsbHub : ScpHub { private readonly UsbDevice[] _devices = new UsbDevice[4]; #region Actions public override bool Open() { for (byte pad = 0; pad < _devices.Length; pad++) { _devices[pad] = new UsbDevice { PadId = (DsPadId)pad }; } return base.Open(); } public override bool Start() { m_Started = true; byte index = 0; // enumerate DS4 devices for (byte instance = 0; instance < _devices.Length && index < _devices.Length; instance++) { try { UsbDevice current = new UsbDs4(); current.PadId = (DsPadId)index; if (current.Open(instance)) { if (LogArrival(current)) { current.HidReportReceived += OnHidReportReceived; _devices[index++] = current; } else current.Close(); } else current.Close(); } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); break; } } // enumerate DS3 devices for (byte instance = 0; instance < _devices.Length && index < _devices.Length; instance++) { try { UsbDevice current = new UsbDs3(); current.PadId = (DsPadId)index; if (current.Open(instance)) { if (!Apply3RdPartyWorkaroundsForDs3(ref current, instance)) continue; // notify bus of new device if (LogArrival(current)) { // listen for HID reports current.HidReportReceived += OnHidReportReceived; _devices[index++] = current; } else current.Close(); } else current.Close(); } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); break; } } var hidDevices = UsbGenericGamepad.LocalHidDevices; // enumerate generic devices for (byte instance = 0; instance < hidDevices.Count && index < _devices.Length; instance++) { try { // try to create supported gamepad var current = UsbGenericGamepad.DeviceFactory(hidDevices[instance].DevicePath); // try next on fail if (current == null) continue; // try next if opening failed if (!current.Open(hidDevices[instance].DevicePath)) continue; current.PadId = (DsPadId) index; // notify bus of new device if (LogArrival(current)) { // listen for HID reports current.HidReportReceived += OnHidReportReceived; current.Start(); _devices[index++] = current; } else current.Close(); } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); break; } } try { // enumerate disconnected but reserved devices for (index = 0; index < _devices.Length; index++) { if (_devices[index].State == DsState.Reserved) { _devices[index].Start(); } } } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); } return base.Start(); } public override bool Stop() { m_Started = false; try { foreach (var t in _devices.Where(t => t != null && t.State == DsState.Connected)) { t.Stop(); } } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); } return base.Stop(); } public override bool Close() { m_Started = false; try { foreach (var t in _devices.Where(t => t.State == DsState.Connected)) { t.Close(); } } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); } return base.Close(); } public override bool Suspend() { Stop(); Close(); return base.Suspend(); } public override bool Resume() { Open(); Start(); return base.Resume(); } #endregion #region Windows messaging public override DsPadId Notify(ScpDevice.Notified notification, string Class, string path) { Log.DebugFormat("++ Notify [{0}] [{1}] [{2}]", notification, Class, path); var classGuid = Guid.Parse(Class); switch (notification) { case ScpDevice.Notified.Arrival: { var arrived = new UsbDevice(); if (classGuid == UsbDs3.DeviceClassGuid) { arrived = new UsbDs3(); Log.Info("DualShock 3 plugged in via Usb"); } if (classGuid == UsbDs4.DeviceClassGuid) { arrived = new UsbDs4(); Log.Info("DualShock 4 plugged in via Usb"); } if (classGuid == UsbGenericGamepad.DeviceClassGuid) { arrived = UsbGenericGamepad.DeviceFactory(path); // unknown or unsupported device if (arrived == null) break; Log.Debug("Generic Gamepad plugged in via Usb"); } Log.DebugFormat("Arrival event for GUID {0} received", classGuid); if (arrived.Open(path)) { Log.DebugFormat("Device MAC address: {0}", arrived.DeviceAddress.AsFriendlyName()); if (!Apply3RdPartyWorkaroundsForDs3(ref arrived, path: path)) break; if (LogArrival(arrived)) { if (_devices[(byte) arrived.PadId].IsShutdown) { _devices[(byte) arrived.PadId].IsShutdown = false; _devices[(byte) arrived.PadId].Close(); _devices[(byte) arrived.PadId] = arrived; return arrived.PadId; } arrived.HidReportReceived += OnHidReportReceived; _devices[(byte) arrived.PadId].Close(); _devices[(byte) arrived.PadId] = arrived; if (m_Started) arrived.Start(); return arrived.PadId; } } else { Log.FatalFormat("Couldn't open device {0}", path); } arrived.Close(); } break; case ScpDevice.Notified.Removal: { foreach (var t in _devices.Where(t => t.State == DsState.Connected && path == t.Path)) { Log.InfoFormat("Device with MAC address {0} unplugged from Usb", t.DeviceAddress.AsFriendlyName()); // play disconnect sound if (GlobalConfiguration.Instance.IsUsbDisconnectSoundEnabled) AudioPlayer.Instance.PlayCustomFile(GlobalConfiguration.Instance.UsbDisconnectSoundFile); t.Stop(); } } break; } return DsPadId.None; } #endregion #region Ctors public UsbHub() { InitializeComponent(); } public UsbHub(IContainer container) { container.Add(this); InitializeComponent(); } #endregion } }