-
Notifications
You must be signed in to change notification settings - Fork 0
/
UsbSession.cs
261 lines (227 loc) · 9.68 KB
/
UsbSession.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
using LibUsbDotNet;
using LibUsbDotNet.Main;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace UsbSession
{
public class UsbSession
{
private static UsbDevice Device;
private UsbEndpointReader reader;
private UsbEndpointWriter writer;
private UsbDeviceFinder Finder;
/// <summary>
/// Constructor looks for a single Crestron device connected to system and initializes resources for interfacing with it
/// </summary>
/// <throws>
/// Exception thrown if there are no Crestron devices or more than one Crestron device. Currently the library doesn't work with multiple Crestron USB devices
/// </throws>
public UsbSession()
{
UsbRegDeviceList AllDevices = UsbDevice.AllDevices;
UsbRegDeviceList CrestronDevices = AllDevices.FindAll( d => (Regex.Match(d.Name,"Crestron*").Success || (d.Vid == 0x14BE) ) );
if (CrestronDevices.Count == 0)
{
throw new Exception("No Crestron Devices Present");
}
else if (CrestronDevices.Count > 1)
{
throw new Exception("More than one Crestron device present, please only connect the one you want to connect to");
}
else
{
//RMC3 has a device ID of 0x9
//Will document others here.
//Crestron Vendor ID is 0x14BE
Finder = new UsbDeviceFinder(0x14BE);
Device = UsbDevice.OpenUsbDevice(Finder);
}
}
/// <summary>
/// Opens a single Crestron USB device for sending a command to. Does not force flush buffers.
/// </summary>
public void Open()
{
try
{
// If the device is open and ready
if (Device == null) throw new Exception("Device Not Found.");
// If this is a "whole" usb device (libusb-win32, linux libusb)
// it will have an IUsbDevice interface. If not (WinUSB) the
// variable will be null indicating this is an interface of a
// device.
IUsbDevice wholeUsbDevice = Device as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// This is a "whole" USB device. Before it can be used,
// the desired configuration and interface must be selected.
// Select config #1
wholeUsbDevice.SetConfiguration(1);
// Claim interface #0.
wholeUsbDevice.ClaimInterface(0);
}
writer = Device.OpenEndpointWriter(WriteEndpointID.Ep02); //02
reader = Device.OpenEndpointReader(ReadEndpointID.Ep01); //129, but looks like crestron also supports 131?
//reader = Device.OpenEndpointReader(ReadEndpointID.Ep03); //131
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Flush the Read buffer for the USB device
/// </summary>
public void ClearReadBuffer()
{
if (Device == null || !Device.IsOpen)
{
throw new Exception("Open the device before trying to clear the buffer");
}
reader.ReadFlush();
}
/// <summary>
/// Invokes a command on the console of the Crestron device.
/// </summary>
/// <param name="Command">Console command, as typed out on the console. Do not add a LF or CR at the end.</param>
/// <param name="Prompt"> Specifies the prompt to wait for in the response. This may be a character or a string
/// but must be entered using a regular expression. Defaults to the right angle bracket '>'.</param>
/// <returns>Output of the command to console until the "prompt" is encountered</returns>
public string Invoke(string Command, string Prompt = ">")
{
try
{
if (Device == null || !Device.IsOpen)
{
throw new Exception("Open the device before invoking a command");
}
//Convert the Prompt to a regex
Regex regex = new Regex(Prompt);
string TerminatedCommand = Command + "\r\n";
string response = "";
//Write the command
ErrorCode ec = writer.Write(Encoding.ASCII.GetBytes(TerminatedCommand), 3000, out int bytesWritten);
if (ec != ErrorCode.None) throw new Exception("Writer error");// switchUsbDevice.LastErrorString);
//Read the response
//According to libusb mailing lists they suggest this buffer be a multiple of the endpoint interface
//max transfer size (512 in this case)
byte[] readBuffer = new byte[1024];
while (ec == ErrorCode.None)
{
// If the device hasn't sent data in the last 5000 milliseconds,
// a timeout error (ec = IoTimedOut) will occur.
// Was originally 100 ms but it seems that when authentication is enabled it can cause large read delays.
ec = reader.Read(readBuffer, 5000, out int bytesRead);
//Don't want to throw this exception, authentication may be enabled!
//if (bytesRead == 0) throw new Exception("No more bytes!");
if (bytesRead == 0)
{
return response;
}
// Write that output to the console.
//Console.Write(Encoding.Default.GetString(readBuffer, 0, bytesRead));
string newChar = Encoding.ASCII.GetString(readBuffer, 0, bytesRead);
response += newChar;
if (regex.Match(response).Success)
{
return response;
}
}
return response;
}
catch (Exception ex)
{
this.Close();
throw ex;
}
}
/// <summary>
/// Read bytes the RX buffer.
/// </summary>
/// <returns>Number of bytes read</returns>
public int Read()
{
byte[] readBuffer = new byte[1000];
ErrorCode ec = reader.Read(readBuffer, 5000, out int bytesRead);
if (ec != ErrorCode.None) throw new Exception("Reader error");
return bytesRead;
}
/// <summary>
/// Closes a USB device and disposes of resources and handles. Does not completely free it from the underlying OS - call Exit() for that
/// </summary>
public void Close()
{
if (Device != null)
{
if (Device.IsOpen)
{
// If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
// it exposes an IUsbDevice interface. If not (WinUSB) the
// 'wholeUsbDevice' variable will be null indicating this is
// an interface of a device; it does not require or support
// configuration and interface selection.
IUsbDevice wholeUsbDevice = Device as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// Release interface #0.
wholeUsbDevice.ReleaseInterface(0);
}
Device.Close();
}
Device = null;
// Free usb resources - Similar to unplugging the usb
//UsbDevice.Exit();
}
}
/// <summary>
/// Free the USB Resources - similar to unplugging the usb or "Safely ejecting"
/// Calls close() if the device is still "open"
/// </summary>
public void Exit()
{
if (Device != null)
{
if (Device.IsOpen)
{
// If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
// it exposes an IUsbDevice interface. If not (WinUSB) the
// 'wholeUsbDevice' variable will be null indicating this is
// an interface of a device; it does not require or support
// configuration and interface selection.
IUsbDevice wholeUsbDevice = Device as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// Release interface #0.
wholeUsbDevice.ReleaseInterface(0);
}
Device.Close();
}
Device = null;
// Free usb resources - Similar to unplugging the usb
UsbDevice.Exit();
}
else
{
// Free usb resources - Similar to unplugging the usb
UsbDevice.Exit();
}
}
/// <summary>
/// Test if the USB device has been opened with Open()
/// </summary>
/// <returns>True if the device is Open and ready to send commands to with Invoke(), and false otherwise</returns>
public bool TestSession()
{
if(Device is object)
{
return Device.IsOpen;
}
else
{
return false;
}
}
}
}