Skip to content

Commit 89deaab

Browse files
authored
improve device support (#19)
- add error conversion (VBox -> Linux) - add STALL recovery - add USB 3 support - add alternate interface support - fix reported device speed
1 parent 043a229 commit 89deaab

File tree

8 files changed

+189
-13
lines changed

8 files changed

+189
-13
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ SPDX-License-Identifier: GPL-2.0-only
77
<Project>
88
<ItemGroup>
99
<!-- all -->
10-
<PackageVersion Include="GitVersion.MsBuild" Version="5.6.8" />
10+
<PackageVersion Include="GitVersion.MsBuild" Version="5.6.9" />
1111

1212
<!-- UsbIpServer -->
1313
<PackageVersion Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />

UsbIpServer/AttachedClient.cs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ async Task HandleSubmitAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSubmit submit
4949
{
5050
ep = basic.ep,
5151
dir = (basic.direction == UsbIpDir.USBIP_DIR_IN) ? UsbSupDirection.USBSUP_DIRECTION_IN : UsbSupDirection.USBSUP_DIRECTION_OUT,
52-
flags = (basic.direction == UsbIpDir.USBIP_DIR_IN) ? UsbSupXferFlags.USBSUP_FLAG_SHORT_OK : UsbSupXferFlags.USBSUP_FLAG_NONE,
52+
flags = (basic.direction == UsbIpDir.USBIP_DIR_IN)
53+
? (((submit.transfer_flags & 1) != 0) ? UsbSupXferFlags.USBSUP_FLAG_NONE : UsbSupXferFlags.USBSUP_FLAG_SHORT_OK)
54+
: UsbSupXferFlags.USBSUP_FLAG_NONE,
5355
error = UsbSupError.USBSUP_XFER_OK,
5456
len = submit.transfer_buffer_length,
5557
numIsoPkts = (uint)submit.number_of_packets,
5658
aIsoPkts = new UsbSupIsoPkt[8],
5759
};
5860

61+
var requestLength = submit.transfer_buffer_length;
5962
var payloadOffset = 0;
6063
switch (transferType)
6164
{
@@ -95,17 +98,52 @@ async Task HandleSubmitAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSubmit submit
9598
throw new NotImplementedException("ISO transfers");
9699
}
97100

98-
if ((basic.ep == 0) && (submit.setup.bmRequestType == 0) && (submit.setup.bRequest == UsbRequest.SET_CONFIGURATION))
101+
if ((basic.ep == 0)
102+
&& (submit.setup.bmRequestType == UsbRequestTypeRecipient.DEVICE)
103+
&& (submit.setup.bRequest == UsbRequest.SET_CONFIGURATION))
99104
{
100105
// VBoxUsb needs this to get the endpoint handles
101-
var configurationValue = (byte)submit.setup.wValue;
102-
Logger.LogTrace($"Trapped SET_CONFIGURATION {configurationValue}");
103-
await Device.IoControlAsync(IoControl.SUPUSB_IOCTL_USB_SET_CONFIG, new byte[] { configurationValue }, null);
104-
ConfigurationDescriptors.SetConfiguration(configurationValue);
106+
var setConfig = new UsbSupSetConfig()
107+
{
108+
bConfigurationValue = (byte)submit.setup.wValue,
109+
};
110+
Logger.LogDebug($"Trapped SET_CONFIGURATION: {setConfig.bConfigurationValue}");
111+
await Device.IoControlAsync(IoControl.SUPUSB_IOCTL_USB_SET_CONFIG, StructToBytes(setConfig), null);
112+
ConfigurationDescriptors.SetConfiguration(setConfig.bConfigurationValue);
113+
}
114+
else if ((basic.ep == 0)
115+
&& (submit.setup.bmRequestType == UsbRequestTypeRecipient.DEVICE)
116+
&& (submit.setup.bRequest == UsbRequest.SET_INTERFACE))
117+
{
118+
// VBoxUsb needs this to get the endpoint handles
119+
var selectInterface = new UsbSupSelectInterface()
120+
{
121+
bInterfaceNumber = (byte)submit.setup.wIndex,
122+
bAlternateSetting = (byte)submit.setup.wValue,
123+
};
124+
Logger.LogDebug($"Trapped SET_INTERFACE: {selectInterface.bInterfaceNumber} -> {selectInterface.bAlternateSetting}");
125+
await Device.IoControlAsync(IoControl.SUPUSB_IOCTL_USB_SELECT_INTERFACE, StructToBytes(selectInterface), null);
126+
ConfigurationDescriptors.SetInterface(selectInterface.bInterfaceNumber, selectInterface.bAlternateSetting);
127+
}
128+
else if ((basic.ep == 0)
129+
&& (submit.setup.bmRequestType == UsbRequestTypeRecipient.ENDPOINT)
130+
&& (submit.setup.bRequest == UsbRequest.CLEAR_FEATURE)
131+
&& (submit.setup.wValue == 0))
132+
{
133+
// VBoxUsb needs this to notify the host controller
134+
var clearEndpoint = new UsbSupClearEndpoint()
135+
{
136+
bEndpoint = (byte)submit.setup.wIndex,
137+
};
138+
Logger.LogDebug($"Trapped CLEAR_FEATURE: {clearEndpoint.bEndpoint}");
139+
await Device.IoControlAsync(IoControl.SUPUSB_IOCTL_USB_CLEAR_ENDPOINT, StructToBytes(clearEndpoint), null);
105140
}
106141
else
107142
{
108-
Logger.LogTrace($"{submit.setup.bmRequestType} {submit.setup.bRequest} {submit.setup.wValue} {submit.setup.wIndex} {submit.setup.wLength}");
143+
if (transferType == UsbEndpointType.USB_ENDPOINT_TYPE_CONTROL)
144+
{
145+
Logger.LogTrace($"{submit.setup.bmRequestType} {submit.setup.bRequest} {submit.setup.wValue} {submit.setup.wIndex} {submit.setup.wLength}");
146+
}
109147
var gc = GCHandle.Alloc(buf, GCHandleType.Pinned);
110148
try
111149
{
@@ -123,7 +161,7 @@ async Task HandleSubmitAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSubmit submit
123161
basic.command = UsbIpCmd.USBIP_RET_SUBMIT;
124162
var retSubmit = new UsbIpHeaderRetSubmit()
125163
{
126-
status = (int)urb.error,
164+
status = -(int)ConvertError(urb.error),
127165
actual_length = (int)urb.len,
128166
start_frame = submit.start_frame,
129167
number_of_packets = (int)urb.numIsoPkts,
@@ -135,7 +173,12 @@ async Task HandleSubmitAsync(UsbIpHeaderBasic basic, UsbIpHeaderCmdSubmit submit
135173
retSubmit.actual_length = (retSubmit.actual_length > payloadOffset) ? (retSubmit.actual_length - payloadOffset) : 0;
136174
}
137175

138-
Logger.LogTrace($"actual: {retSubmit.actual_length}");
176+
if (urb.error != UsbSupError.USBSUP_XFER_OK)
177+
{
178+
Logger.LogDebug($"{urb.error} -> {ConvertError(urb.error)} -> {retSubmit.status}");
179+
}
180+
Logger.LogTrace($"actual: {retSubmit.actual_length}, requested: {requestLength}");
181+
139182
var retBuf = new byte[48 /* sizeof(usbip_header) */];
140183
BinaryPrimitives.WriteUInt32BigEndian(retBuf.AsSpan(0), (uint)basic.command);
141184
BinaryPrimitives.WriteUInt32BigEndian(retBuf.AsSpan(4), basic.seqnum);

UsbIpServer/ExportedDevice.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,36 @@ public static async Task<ExportedDevice[]> GetAll(CancellationToken cancellation
183183
await hubFile.IoControlAsync(IoControl.IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, buf, buf);
184184
BytesToStruct(buf, 0, out data);
185185

186+
var speed = MapWindowsSpeedToLinuxSpeed(data.Speed);
187+
188+
var data2 = new UsbNodeConnectionInformationExV2()
189+
{
190+
ConnectionIndex = connectionIndex,
191+
Length = (uint)Marshal.SizeOf<UsbNodeConnectionInformationExV2>(),
192+
SupportedUsbProtocols = UsbProtocols.Usb110 | UsbProtocols.Usb200 | UsbProtocols.Usb300,
193+
};
194+
var buf2 = StructToBytes(data2);
195+
await hubFile.IoControlAsync(IoControl.IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, buf2, buf2);
196+
BytesToStruct(buf2, 0, out data2);
197+
198+
if ((data2.SupportedUsbProtocols & UsbProtocols.Usb300) != 0)
199+
{
200+
if ((data2.Flags & UsbNodeConnectionInformationExV2Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher) != 0)
201+
{
202+
speed = Linux.UsbDeviceSpeed.USB_SPEED_SUPER_PLUS;
203+
}
204+
else if ((data2.Flags & UsbNodeConnectionInformationExV2Flags.DeviceIsOperatingAtSuperSpeedOrHigher) != 0)
205+
{
206+
speed = Linux.UsbDeviceSpeed.USB_SPEED_SUPER;
207+
}
208+
}
209+
186210
var exportedDevice = new ExportedDevice()
187211
{
188212
Path = instanceId,
189213
BusNum = hubNum,
190214
DevNum = connectionIndex,
191-
Speed = MapWindowsSpeedToLinuxSpeed(data.Speed),
215+
Speed = speed,
192216
VendorId = data.DeviceDescriptor.idVendor,
193217
ProductId = data.DeviceDescriptor.idProduct,
194218
BcdDevice = data.DeviceDescriptor.bcdDevice,

UsbIpServer/Interop/Linux.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,22 @@ public enum UsbDeviceSpeed : uint
1717
USB_SPEED_SUPER, // usb 3.0
1818
USB_SPEED_SUPER_PLUS, // usb 3.1
1919
}
20+
21+
public enum Errno : int
22+
{
23+
SUCCESS = 0,
24+
/// <summary>linux: errno-base.h</summary>
25+
EPIPE = 32,
26+
/// <summary>linux: errno.h</summary>
27+
ETIME = 62,
28+
/// <summary>linux: errno.h</summary>
29+
EPROTO = 71,
30+
/// <summary>linux: errno.h</summary>
31+
EOVERFLOW = 75,
32+
/// <summary>linux: errno.h</summary>
33+
EILSEQ = 84,
34+
/// <summary>linux: errno.h</summary>
35+
EREMOTEIO = 121,
36+
}
2037
}
2138
}

UsbIpServer/Interop/Usb.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,22 @@ public struct UsbEndpointDescriptor
9191
public byte bInterval;
9292
}
9393

94+
public enum UsbRequestTypeRecipient : byte
95+
{
96+
/// <summary>WinSDK: usbspec.h: BMREQUEST_TO_DEVICE</summary>
97+
DEVICE = 0,
98+
/// <summary>WinSDK: usbspec.h: BMREQUEST_TO_INTERFACE</summary>
99+
INTERFACE,
100+
/// <summary>WinSDK: usbspec.h: BMREQUEST_TO_ENDPOINT</summary>
101+
ENDPOINT,
102+
/// <summary>WinSDK: usbspec.h: BMREQUEST_TO_OTHER</summary>
103+
OTHER,
104+
}
105+
94106
public enum UsbRequest : byte
95107
{
108+
/// <summary>WinSDK: usbspec.h: USB_REQUEST_SET_CONFIGURATION</summary>
109+
CLEAR_FEATURE = 0x01,
96110
/// <summary>WinSDK: usbspec.h: USB_REQUEST_SET_CONFIGURATION</summary>
97111
SET_CONFIGURATION = 0x09,
98112
/// <summary>WinSDK: usbspec.h: USB_REQUEST_SET_INTERFACE</summary>
@@ -103,7 +117,7 @@ public enum UsbRequest : byte
103117
[StructLayout(LayoutKind.Sequential, Pack = 1)]
104118
public struct UsbDefaultPipeSetupPacket
105119
{
106-
public byte bmRequestType;
120+
public UsbRequestTypeRecipient bmRequestType;
107121
public UsbRequest bRequest;
108122
public ushort wValue;
109123
public ushort wIndex;

UsbIpServer/Interop/VBoxUsb.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ public enum IoControl : uint
177177
/// <summary>VBoxUsb: usblib-win.h</summary>
178178
SUPUSB_IOCTL_USB_RESET = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x608 << 2) | (MethodCode.METHOD_BUFFERED),
179179
/// <summary>VBoxUsb: usblib-win.h</summary>
180+
SUPUSB_IOCTL_USB_SELECT_INTERFACE = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x609 << 2) | (MethodCode.METHOD_BUFFERED),
181+
/// <summary>VBoxUsb: usblib-win.h</summary>
180182
SUPUSB_IOCTL_USB_SET_CONFIG = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x60a << 2) | (MethodCode.METHOD_BUFFERED),
181183
/// <summary>VBoxUsb: usblib-win.h</summary>
182184
SUPUSB_IOCTL_USB_CLAIM_DEVICE = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x60b << 2) | (MethodCode.METHOD_BUFFERED),
@@ -185,6 +187,8 @@ public enum IoControl : uint
185187
/// <summary>VBoxUsb: usblib-win.h</summary>
186188
SUPUSB_IOCTL_IS_OPERATIONAL = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x60d << 2) | (MethodCode.METHOD_BUFFERED),
187189
/// <summary>VBoxUsb: usblib-win.h</summary>
190+
SUPUSB_IOCTL_USB_CLEAR_ENDPOINT = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x60e << 2) | (MethodCode.METHOD_BUFFERED),
191+
/// <summary>VBoxUsb: usblib-win.h</summary>
188192
SUPUSB_IOCTL_GET_VERSION = (DeviceType.FILE_DEVICE_UNKNOWN << 16) | (AccessCode.FILE_WRITE_ACCESS << 14) | (0x60f << 2) | (MethodCode.METHOD_BUFFERED),
189193
}
190194

@@ -267,5 +271,27 @@ public struct UsbSupUrb
267271
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
268272
public UsbSupIsoPkt[] aIsoPkts; /* [in/out] isochronous packet descriptors */
269273
}
274+
275+
/// <summary>VBoxUsb: usblib-win.h: USBSUP_SET_CONFIG</summary>
276+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
277+
public struct UsbSupSetConfig
278+
{
279+
public byte bConfigurationValue;
280+
}
281+
282+
/// <summary>VBoxUsb: usblib-win.h: USBSUP_SELECT_INTERFACE</summary>
283+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
284+
public struct UsbSupSelectInterface
285+
{
286+
public byte bInterfaceNumber;
287+
public byte bAlternateSetting;
288+
}
289+
290+
/// <summary>VBoxUsb: usblib-win.h: USBSUP_CLEAR_ENDPOINT</summary>
291+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
292+
public struct UsbSupClearEndpoint
293+
{
294+
public byte bEndpoint;
295+
}
270296
}
271297
}

UsbIpServer/Interop/WinSDK.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,10 @@ public enum FunctionCode : ushort
190190
{
191191
/// <summary>WinSDK: usbiodef.h</summary>
192192
USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 260,
193-
194193
/// <summary>WinSDK: usbiodef.h</summary>
195194
USB_GET_NODE_CONNECTION_INFORMATION_EX = 274,
195+
/// <summary>WinSDK: usbiodef.h</summary>
196+
USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 = 279,
196197
}
197198

198199
public enum MethodCode : byte
@@ -218,6 +219,9 @@ public enum IoControl : uint
218219
/// <summary>WinSDK: usbioctl.h</summary>
219220
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = (DeviceType.FILE_DEVICE_USB << 16) | (AccessCode.FILE_ANY_ACCESS << 14)
220221
| (FunctionCode.USB_GET_NODE_CONNECTION_INFORMATION_EX << 2) | (MethodCode.METHOD_BUFFERED),
222+
223+
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 = (DeviceType.FILE_DEVICE_USB << 16) | (AccessCode.FILE_ANY_ACCESS << 14)
224+
| (FunctionCode.USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 << 2) | (MethodCode.METHOD_BUFFERED),
221225
}
222226

223227
/// <summary>WinSDK: usbioctl.h: USB_DESCRIPTOR_REQUEST</summary>
@@ -262,5 +266,35 @@ public struct UsbNodeConnectionInformationEx
262266
public uint ConnectionStatus;
263267
/* USB_PIPE_INFO PipeList[0]; */
264268
}
269+
270+
/// <summary>WinSDK: usbioctl.h: USB_PROTOCOLS</summary>
271+
[Flags]
272+
public enum UsbProtocols : uint
273+
{
274+
None = 0,
275+
Usb110 = (1 << 0),
276+
Usb200 = (1 << 1),
277+
Usb300 = (1 << 2),
278+
}
279+
280+
/// <summary>WinSDK: usbioctl.h: USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS</summary>
281+
[Flags]
282+
public enum UsbNodeConnectionInformationExV2Flags : uint
283+
{
284+
DeviceIsOperatingAtSuperSpeedOrHigher = (1 << 0),
285+
DeviceIsSuperSpeedCapableOrHigher = (1 << 1),
286+
DeviceIsOperatingAtSuperSpeedPlusOrHigher = (1 << 2),
287+
DeviceIsSuperSpeedPlusCapableOrHigher = (1 << 3),
288+
}
289+
290+
/// <summary>WinSDK: usbioctl.h: USB_NODE_CONNECTION_INFORMATION_EX_V2</summary>
291+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
292+
public struct UsbNodeConnectionInformationExV2
293+
{
294+
public uint ConnectionIndex;
295+
public uint Length;
296+
public UsbProtocols SupportedUsbProtocols;
297+
public UsbNodeConnectionInformationExV2Flags Flags;
298+
}
265299
}
266300
}

UsbIpServer/Tools.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,5 +268,23 @@ public static void GetBusId(SafeDeviceInfoSetHandle deviceInfoSet, in SpDevInfoD
268268
throw new NotSupportedException($"DEVPKEY_Device_LocationInfo returned unexpected {nameof(connectionIndex)} 0");
269269
}
270270
}
271+
272+
/// <summary>
273+
/// See <see href="https://www.kernel.org/doc/html/latest/driver-api/usb/error-codes.html"/>.
274+
/// </summary>
275+
public static Linux.Errno ConvertError(VBoxUsb.UsbSupError usbSupError)
276+
{
277+
return usbSupError switch
278+
{
279+
VBoxUsb.UsbSupError.USBSUP_XFER_OK => Linux.Errno.SUCCESS,
280+
VBoxUsb.UsbSupError.USBSUP_XFER_STALL => Linux.Errno.EPIPE,
281+
VBoxUsb.UsbSupError.USBSUP_XFER_DNR => Linux.Errno.ETIME,
282+
VBoxUsb.UsbSupError.USBSUP_XFER_CRC => Linux.Errno.EILSEQ,
283+
VBoxUsb.UsbSupError.USBSUP_XFER_NAC => Linux.Errno.EPROTO,
284+
VBoxUsb.UsbSupError.USBSUP_XFER_UNDERRUN => Linux.Errno.EREMOTEIO,
285+
VBoxUsb.UsbSupError.USBSUP_XFER_OVERRUN => Linux.Errno.EOVERFLOW,
286+
_ => Linux.Errno.EPROTO,
287+
};
288+
}
271289
}
272290
}

0 commit comments

Comments
 (0)