ScpControl/Usb/Gamepads/UsbGenericGamepad.cs (152 lines of code) (raw):

using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Threading; using System.Threading.Tasks; using HidSharp; using ScpControl.Profiler; using ScpControl.ScpCore; using ScpControl.Shared.Core; using ScpControl.Sound; using ScpControl.Utilities; namespace ScpControl.Usb.Gamepads { public class UsbGenericGamepad : UsbDevice { #region Private fields private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private HidDevice _currentHidDevice; private HidStream _currentHidStream; #endregion #region Ctor protected UsbGenericGamepad() { // TODO: Generic isn't currently supported by bus driver Model = DsModel.DS3; } #endregion #region Properties /// <summary> /// GUID_DEVINTERFACE_HID /// </summary> public static Guid DeviceClassGuid { // https://msdn.microsoft.com/en-us/library/windows/hardware/ff545860(v=vs.85).aspx get { return Guid.Parse("{4D1E55B2-F16F-11CF-88CB-001111000030}"); } } public static IList<HidDevice> LocalHidDevices { get { return new HidDeviceLoader().GetDevices().ToList(); } } #endregion #region Object factory public static UsbDevice DeviceFactory(string devicePath) { short vid, pid; GetHardwareId(devicePath, out vid, out pid); #region DragonRise Inc. Usb Gamepad SNES if (vid == 0x0079 && pid == 0x0011) { Log.InfoFormat( "DragonRise Inc. Usb Gamepad SNES detected [VID: {0:X4}] [PID: {1:X4}]", vid, pid); return new UsbSnesGamepad(); } #endregion #region LSI Logic Gamepad if (vid == 0x0079 && pid == 0x0006) { Log.InfoFormat( "LSI Logic Gamepad detected [VID: {0:X4}] [PID: {1:X4}]", vid, pid); return new UsbLsiLogicGamepad(); } #endregion #region ShanWan Wireless Gamepad if (vid == 0x2563 && pid == 0x0523) { Log.InfoFormat( "ShanWan Wireless Gamepad detected [VID: {0:X4}] [PID: {1:X4}]", vid, pid); return new UsbShanWanWirelessGamepad(); } #endregion #region GameStop PC Advanced Controller if (vid == 0x11FF && pid == 0x3331) { Log.InfoFormat( "GameStop PC Advanced Controller detected [VID: {0:X4}] [PID: {1:X4}]", vid, pid); return new UsbGameStopPcAdvanced(); } #endregion return null; } #endregion #region Actions public override bool Open(string devicePath) { short vid, pid; GetHardwareId(devicePath, out vid, out pid); var loader = new HidDeviceLoader(); // search for HID _currentHidDevice = loader.GetDevices(vid, pid).FirstOrDefault(); if (_currentHidDevice == null) { Log.ErrorFormat("Couldn't find device with VID: {0}, PID: {1}", vid, pid); return false; } // open HID if (!_currentHidDevice.TryOpen(out _currentHidStream)) { Log.ErrorFormat("Couldn't open device {0}", _currentHidDevice); return false; } // since these devices have no MAC address, generate one DeviceAddress = PhysicalAddress.Parse(MacAddressGenerator.NewMacAddress); IsActive = true; Path = devicePath; return IsActive; } public override bool Start() { // TODO: remove duplicate code if (!IsActive) return State == DsState.Connected; State = DsState.Connected; PacketCounter = 0; Task.Factory.StartNew(MainHidInputReader, _cancellationTokenSource.Token); // connection sound if (GlobalConfiguration.Instance.IsUsbConnectSoundEnabled) AudioPlayer.Instance.PlayCustomFile(GlobalConfiguration.Instance.UsbConnectSoundFile); return State == DsState.Connected; } public override bool Stop() { _cancellationTokenSource.Cancel(); _cancellationTokenSource = new CancellationTokenSource(); // pad reservation not supported State = DsState.Disconnected; return true; } #endregion #region Main worker private void MainHidInputReader(object o) { var token = (CancellationToken) o; var buffer = new byte[_currentHidDevice.MaxInputReportLength]; while (!token.IsCancellationRequested) { var count = _currentHidStream.Read(buffer, 0, buffer.Length); if (count > 0) { ParseHidReport(buffer); } } } #endregion #region Unused methods protected override void Process(DateTime now) { // ignore } public override bool Pair(PhysicalAddress master) { return true; // ignore } public override bool Rumble(byte large, byte small) { return true; // ignore } #endregion } }