Currently I ran into an issue revolving around the USBClient RawDevice and a write timeout.
Here is the relevant code:
namespace HotasExpansion
{
public class UsbClient
{
public static ConfiguredDevice ConfigureUsbDevice()
{
ushort myVID = 0x1234;
ushort myPID = 0x0012;
ushort myDeviceVersion = 0x100;
ushort myDeviceMaxPower = 500; // in milli amps
string companyName = "Something Something";
string productName = "HOTAS Throttle";
string myDeviceSerialNumber = "0";
RawDevice device = new RawDevice(myVID, myPID, myDeviceVersion, myDeviceMaxPower, companyName, productName, myDeviceSerialNumber);
// Endpoints
ConfiguredEndpoint endPoints = ConfigureEndpoints(device);
ConfigureMouseDevice(device, endPoints);
ConfigureJoystickDevice(device, endPoints);
return new ConfiguredDevice { Device = device, EndPoint = endPoints };
}
private static ConfiguredEndpoint ConfigureEndpoints(RawDevice device)
{
byte mouseWriteEPNumber = (byte)device.ReserveNewEndpoint();
byte mouseReadEPNumber = (byte)device.ReserveNewEndpoint();
MS.Configuration.Endpoint[] mouseEpDesc = {
new MS.Configuration.Endpoint(mouseWriteEPNumber, MS.Configuration.Endpoint.ATTRIB_Write | MS.Configuration.Endpoint.ATTRIB_Interrupt),
new MS.Configuration.Endpoint(mouseReadEPNumber, MS.Configuration.Endpoint.ATTRIB_Read | MS.Configuration.Endpoint.ATTRIB_Interrupt),
};
mouseEpDesc[0].wMaxPacketSize = 32;
mouseEpDesc[0].bInterval = 10;
mouseEpDesc[1].wMaxPacketSize = 32;
mouseEpDesc[1].bInterval = 10;
byte joystickWriteEPNumber = (byte)device.ReserveNewEndpoint();
byte joystickReadEPNumber = (byte)device.ReserveNewEndpoint();
MS.Configuration.Endpoint[] joystickEpDesc = {
new MS.Configuration.Endpoint(joystickWriteEPNumber, MS.Configuration.Endpoint.ATTRIB_Write | MS.Configuration.Endpoint.ATTRIB_Interrupt),
new MS.Configuration.Endpoint(joystickReadEPNumber, MS.Configuration.Endpoint.ATTRIB_Read | MS.Configuration.Endpoint.ATTRIB_Interrupt),
};
joystickEpDesc[0].wMaxPacketSize = 16;
joystickEpDesc[0].bInterval = 10;
joystickEpDesc[1].wMaxPacketSize = 16;
joystickEpDesc[1].bInterval = 10;
return new ConfiguredEndpoint {
MouseWriteEndpointNumber = mouseWriteEPNumber,
MouseReadEndpointNumber = mouseReadEPNumber,
MouseEndPointDescriptor = mouseEpDesc,
JoystickWriteEndpointNumber = joystickWriteEPNumber,
JoystickReadEndpointNumber = joystickReadEPNumber,
JoystickEndPointDescriptor = joystickEpDesc
};
}
private static void ConfigureMouseDevice(RawDevice device, ConfiguredEndpoint endPoints)
{
byte[] hidMouseReportDescriptorPayload = GetMouseHidReportDescriptorPayload();
// Class Descriptor
var mouseClassDescriptorPayload = GetHidClassDescriptorPayload(((byte)hidMouseReportDescriptorPayload.Length));
// HID class descriptor
MS.Configuration.ClassDescriptor hidMouseClassDescriptor = new MS.Configuration.ClassDescriptor(HID_DESCRIPTOR_TYPE, mouseClassDescriptorPayload);
// Interface
MS.Configuration.UsbInterface usbMouseInterface = new MS.Configuration.UsbInterface(0, endPoints.MouseEndPointDescriptor);
usbMouseInterface.classDescriptors = new MS.Configuration.ClassDescriptor[] { hidMouseClassDescriptor };
usbMouseInterface.bInterfaceClass = 0x03; // HID
usbMouseInterface.bInterfaceSubClass = 0x00;
usbMouseInterface.bInterfaceProtocol = 0x02;
// Attach interface
byte interfaceIndex = device.AddInterface(usbMouseInterface, "Hotas Track Ball/KeyPad");
MS.Configuration.GenericDescriptor hidMouseGenericReportDescriptor = new MS.Configuration.GenericDescriptor(0x81, 0x2200, hidMouseReportDescriptorPayload);
hidMouseGenericReportDescriptor.bRequest = 0x06; // GET_DESCRIPTOR
hidMouseGenericReportDescriptor.wIndex = 0x00; // INTERFACE 0 (zero)
// attach descriptor
device.AddDescriptor(hidMouseGenericReportDescriptor);
}
public static byte[] GetMouseHidReportDescriptorPayload()
{
return new byte[]
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
// ------------------------------ Buttons
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x20, // USAGE_MAXIMUM (Button 32)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x20, // REPORT_COUNT (32)
0x81, 0x02, // INPUT (Data,Var,Abs)
// ------------------------------ Buttons
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x21, // USAGE_MINIMUM (Button 33)
0x29, 0x29, // USAGE_MAXIMUM (Button 41)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x9, // REPORT_COUNT (9)
0x81, 0x02, // INPUT (Data,Var,Abs)
// ------------------------------ Padding
0x75, 0x17, // REPORT_SIZE (23)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
// ------------------------------ X,Y position
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x17, 0x01, 0x00, 0x00, 0x80, // LOGICAL_MINIMUM (-2147483647)
0x27, 0xff, 0xff, 0xff, 0x7f, // LOGICAL_MAXIMUM (2147483647)
0x75, 0x20, // REPORT_SIZE (32)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
0xc0
};
}
private static void ConfigureJoystickDevice(RawDevice device, ConfiguredEndpoint endPoints)
{
byte[] hidJoystickReportDescriptorPayload = GetJoystickHidReportDescriptorPayload();
// Class Descriptor
var joystickClassDescriptorPayload = GetHidClassDescriptorPayload(((byte)hidJoystickReportDescriptorPayload.Length));
// HID class descriptor
MS.Configuration.ClassDescriptor hidJoystickClassDescriptor = new MS.Configuration.ClassDescriptor(HID_DESCRIPTOR_TYPE, joystickClassDescriptorPayload);
// Interface
MS.Configuration.UsbInterface usbJoystickInterface = new MS.Configuration.UsbInterface(1, endPoints.JoystickEndPointDescriptor);
usbJoystickInterface.classDescriptors = new MS.Configuration.ClassDescriptor[] { hidJoystickClassDescriptor };
usbJoystickInterface.bInterfaceClass = 0x03; // HID
usbJoystickInterface.bInterfaceSubClass = 0x00;
usbJoystickInterface.bInterfaceProtocol = 0x00;
// Attach interface
byte interfaceIndex = device.AddInterface(usbJoystickInterface, "Hotas Joystick");
MS.Configuration.GenericDescriptor hidJoystickGenericReportDescriptor = new MS.Configuration.GenericDescriptor(0x81, 0x2200, hidJoystickReportDescriptorPayload);
hidJoystickGenericReportDescriptor.bRequest = 0x06; // GET_DESCRIPTOR
hidJoystickGenericReportDescriptor.wIndex = 0x01; // INTERFACE 0 (zero)
// attach descriptor
device.AddDescriptor(hidJoystickGenericReportDescriptor);
}
public static byte[] GetJoystickHidReportDescriptorPayload()
{
return new byte[]
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Joystick)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x10, // USAGE_MAXIMUM (Button 16)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x10, // REPORT_COUNT (6)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END COLLECTION
0xc0 // END COLLECTION
};
}
public static byte[] GetHidClassDescriptorPayload(byte ReportPayLoadLength)
{
return new byte[]
{
0x01, 0x01, // bcdHID (v1.01)
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType (report)
ReportPayLoadLength, 0x00 // wDescriptorLength (report descriptor size in bytes)
};
}
}
public class ConfiguredEndpoint
{
public byte MouseWriteEndpointNumber { get; set; }
public byte MouseReadEndpointNumber { get; set; }
public byte JoystickWriteEndpointNumber { get; set; }
public byte JoystickReadEndpointNumber { get; set; }
public MS.Configuration.Endpoint[] MouseEndPointDescriptor { get; set; }
public MS.Configuration.Endpoint[] JoystickEndPointDescriptor { get; set; }
}
public class ConfiguredDevice
{
public RawDevice Device;
public ConfiguredEndpoint EndPoint;
}
}
I then call this in my Program too create two streams for my two interfaces.
try
{
Device = UsbClient.ConfigureUsbDevice();
Controller.ActiveDevice = Device.Device;
MouseStream = Device.Device.CreateStream(Device.EndPoint.MouseWriteEndpointNumber, Device.EndPoint.MouseReadEndpointNumber);
JoystickStream = Device.Device.CreateStream(Device.EndPoint.JoystickWriteEndpointNumber, Device.EndPoint.JoystickReadEndpointNumber);
}
catch (Exception e)
{
Debug.Print(
"Client stream could not be created due to exception " +
e.Message);
return;
}
This results in a device created in windows that is both a mouse and a gamepad controller. I am able to open the properties and verify they are initialized correctly and functioning.
Later in my code i call something like this every 10ms:
foreach (var byteValue in mouseReport)
{
MouseStream.WriteByte(byteValue);
}
MouseStream.Flush();
In that code I am iterating through a byte array of mouse data that is outputted according to the report descriptor.
However when MouseStream.WriteByte(byteValue) is called i am getting a timeout on the after the first few writes. From that point on every write fails.
Using mainboard G120II version 2.0
#### Exception System.InvalidOperationException - 0x00000000 (4) ####
#### Message: Operation timed out.
#### GHI.Usb.Client.RawDevice+RawStream::Write [IP: 007d] ####
#### System.IO.Stream::WriteByte [IP: 0010] ####
#### HotasExpansion.Program::Report_Frame [IP: 0020] ####
A first chance exception of type ‘System.InvalidOperationException’ occurred in GHI.Usb.dll
An unhandled exception of type ‘System.InvalidOperationException’ occurred in GHI.Usb.dll
Additional information: Operation timed out.
Debugging over serial shows me that the stream was created and initialized successfully and the Stream state indicates that it can read and write.
I am using v4.3.5.0 of The GHI.Usb library and .net micro 4.3.1 on a G120hdrII