1 //Simplified BSD License (BSD-2-Clause) 2 // 3 //Copyright (c) 2021, Marc Riera, The OpenBVE Project 4 // 5 //Redistribution and use in source and binary forms, with or without 6 //modification, are permitted provided that the following conditions are met: 7 // 8 //1. Redistributions of source code must retain the above copyright notice, this 9 // list of conditions and the following disclaimer. 10 //2. Redistributions in binary form must reproduce the above copyright notice, 11 // this list of conditions and the following disclaimer in the documentation 12 // and/or other materials provided with the distribution. 13 // 14 //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 //DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 //ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 25 using LibUsbDotNet; 26 using LibUsbDotNet.Main; 27 using OpenBveApi; 28 using OpenBveApi.Interface; 29 using System; 30 using System.Collections.Generic; 31 using System.Globalization; 32 using System.Threading; 33 34 namespace DenshaDeGoInput 35 { 36 /// <summary> 37 /// Class LibUsb-related functions. 38 /// </summary> 39 internal partial class LibUsb 40 { 41 /// <summary> 42 /// Dictionary containing the supported USB controllers 43 /// </summary> 44 private static Dictionary<Guid, UsbController> supportedUsbControllers = new Dictionary<Guid, UsbController>(); 45 46 /// <summary> 47 /// GUID of the active controller 48 /// </summary> 49 private static Guid activeControllerGuid = new Guid(); 50 51 /// <summary> 52 /// The thread which spins to poll for LibUsb input 53 /// </summary> 54 internal static Thread LibUsbThread; 55 56 /// <summary> 57 /// The control variable for the LibUsb input thread 58 /// </summary> 59 internal static bool LibUsbShouldLoop = true; 60 61 /// <summary> 62 /// The setup packet needed to send data to the controller. 63 /// </summary> 64 private static UsbSetupPacket setupPacket = new UsbSetupPacket(0x40, 0x09, 0x0301, 0x0000, 0x0008); 65 66 /// <summary> 67 /// Adds the supported controller models to the LibUsb list. 68 /// </summary> 69 /// <param name="ids">A list of VID+PID identifiers to search</param> AddSupportedControllers(string[] ids)70 internal static void AddSupportedControllers(string[] ids) 71 { 72 foreach (string id in ids) 73 { 74 int vid = int.Parse(id.Substring(0, 4), NumberStyles.HexNumber); 75 int pid = int.Parse(id.Substring(5, 4), NumberStyles.HexNumber); 76 Guid guid; 77 switch (DenshaDeGoInput.CurrentHost.Platform) 78 { 79 case OpenBveApi.Hosts.HostPlatform.MicrosoftWindows: 80 guid = new Guid(id.Substring(5, 4) + id.Substring(0, 4) + "-ffff-ffff-ffff-ffffffffffff"); 81 break; 82 default: 83 string vendor = id.Substring(2, 2) + id.Substring(0, 2); 84 string product = id.Substring(7, 2) + id.Substring(5, 2); 85 guid = new Guid("ffffffff-" + vendor + "-ffff-" + product + "-ffffffffffff"); 86 break; 87 } 88 UsbController controller = new UsbController(vid, pid); 89 if (!supportedUsbControllers.ContainsKey(guid)) 90 { 91 // Add new controller 92 supportedUsbControllers.Add(guid, controller); 93 } 94 else 95 { 96 // Replace existing controller 97 supportedUsbControllers[guid] = controller; 98 } 99 } 100 } 101 102 /// <summary> 103 /// Loop to be executed on a separate thread to handle LibUsb work. 104 /// </summary> LibUsbLoop()105 internal static void LibUsbLoop() 106 { 107 while (LibUsbShouldLoop && !DenshaDeGoInput.LibUsbIssue) 108 { 109 // First, let's check which USB devices are connected 110 CheckConnectedControllers(); 111 112 if (activeControllerGuid != InputTranslator.ActiveControllerGuid) 113 { 114 if (supportedUsbControllers.ContainsKey(activeControllerGuid)) 115 { 116 // If the selected controller has changed, unload the previous one 117 supportedUsbControllers[activeControllerGuid].Unload(); 118 } 119 activeControllerGuid = InputTranslator.ActiveControllerGuid; 120 } 121 122 // If the current controller is a supported controller and is connected, poll it for input 123 if (supportedUsbControllers.ContainsKey(activeControllerGuid) && supportedUsbControllers[activeControllerGuid].IsConnected) 124 { 125 supportedUsbControllers[activeControllerGuid].Poll(); 126 } 127 } 128 129 foreach (var controller in supportedUsbControllers.Values) 130 { 131 controller.Unload(); 132 } 133 } 134 135 /// <summary> 136 /// Checks the connection status of supported LibUsb controllers. 137 /// </summary> CheckConnectedControllers()138 private static void CheckConnectedControllers() 139 { 140 if (DenshaDeGoInput.LibUsbIssue) 141 { 142 return; 143 } 144 try 145 { 146 foreach (UsbController controller in supportedUsbControllers.Values) 147 { 148 if (controller.ControllerDevice == null || !controller.IsConnected) 149 { 150 // The device is not configured, try to find it 151 controller.ControllerDevice = UsbDevice.OpenUsbDevice(new UsbDeviceFinder(controller.VendorID, controller.ProductID)); 152 } 153 if (controller.ControllerDevice == null) 154 { 155 // The controller is not connected 156 controller.IsConnected = false; 157 } 158 else 159 { 160 if (!controller.IsConnected) 161 { 162 // Open endpoint reader, if necessary 163 controller.ControllerReader = controller.ControllerDevice.OpenEndpointReader(ReadEndpointID.Ep01); 164 } 165 // The controller is connected 166 controller.IsConnected = true; 167 } 168 } 169 } 170 catch (Exception ex) 171 { 172 Console.WriteLine(ex.StackTrace); 173 if (DenshaDeGoInput.CurrentHost.SimulationState == SimulationState.Running) 174 { 175 DenshaDeGoInput.CurrentHost.AddMessage(MessageType.Error, false, "The DenshaDeGo! Input Plugin encountered a critical error whilst attempting to update the connected controller list."); 176 } 177 //LibUsb isn't working right 178 DenshaDeGoInput.LibUsbIssue = true; 179 } 180 } 181 182 /// <summary> 183 /// Gets the list of supported controllers. 184 /// </summary> 185 /// <returns>The list of supported controllers.</returns> GetSupportedControllers()186 internal static Dictionary<Guid, UsbController> GetSupportedControllers() 187 { 188 return supportedUsbControllers; 189 } 190 191 /// <summary> 192 /// Syncs the read and input buffers for the controller with the specified GUID. 193 /// </summary> 194 /// <param name="guid">The GUID of the controller</param> 195 /// <param name="read">An array containing the previous read buffer</param> 196 /// <param name="write">The bytes to be sent to the controller</param> 197 /// <returns>The bytes read from the controller.</returns> SyncController(Guid guid, byte[] read, byte[] write)198 internal static byte[] SyncController(Guid guid, byte[] read, byte[] write) 199 { 200 // If the read buffer's length is 0, copy the initial input bytes to get the required read length 201 if (supportedUsbControllers[guid].ReadBuffer.Length == 0) 202 { 203 supportedUsbControllers[guid].ReadBuffer = read; 204 } 205 // Copy the output bytes to the write buffer 206 supportedUsbControllers[guid].WriteBuffer = write; 207 // If the length of the byte array to be sent to the controller when unloaded is 0, use the write buffer 208 if (supportedUsbControllers[guid].UnloadBuffer.Length == 0) 209 { 210 supportedUsbControllers[guid].UnloadBuffer = write; 211 } 212 // Return the bytes from the read buffer 213 return supportedUsbControllers[guid].ReadBuffer; 214 } 215 216 } 217 } 218