Skip to content

Commit 469d2a7

Browse files
committed
Windows: composite devices
1 parent 1e5af64 commit 469d2a7

31 files changed

+1393
-157
lines changed

java-does-usb/jextract/windows/gen_win.cmd

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,23 @@ call %JEXTRACT% --source --output ../../src/main/java ^
1313
--include-function DeviceIoControl ^
1414
--include-function GetLastError ^
1515
--include-function GetModuleHandleW ^
16+
--include-function FormatMessageW ^
17+
--include-function LocalFree ^
1618
--include-macro ERROR_SUCCESS ^
1719
--include-macro ERROR_NO_MORE_ITEMS ^
20+
--include-macro ERROR_MORE_DATA ^
1821
--include-macro ERROR_INSUFFICIENT_BUFFER ^
22+
--include-macro ERROR_FILE_NOT_FOUND ^
1923
--include-macro GENERIC_READ ^
2024
--include-macro GENERIC_WRITE ^
2125
--include-macro FILE_SHARE_READ ^
2226
--include-macro FILE_SHARE_WRITE ^
2327
--include-macro FILE_ATTRIBUTE_NORMAL ^
2428
--include-macro FILE_FLAG_OVERLAPPED ^
2529
--include-macro OPEN_EXISTING ^
30+
--include-macro FORMAT_MESSAGE_ALLOCATE_BUFFER ^
31+
--include-macro FORMAT_MESSAGE_FROM_SYSTEM ^
32+
--include-macro FORMAT_MESSAGE_IGNORE_INSERTS ^
2633
--include-struct _GUID ^
2734
--include-typedef GUID ^
2835
windows_headers.h
@@ -42,6 +49,9 @@ call %JEXTRACT% --source --output ../../src/main/java ^
4249
--include-function SetupDiGetDeviceRegistryPropertyW ^
4350
--include-function SetupDiGetDevicePropertyW ^
4451
--include-function SetupDiOpenDeviceInterfaceW ^
52+
--include-function SetupDiCreateDeviceInfoList ^
53+
--include-function SetupDiOpenDeviceInfoW ^
54+
--include-function SetupDiOpenDevRegKey ^
4555
--include-struct _SP_DEVINFO_DATA ^
4656
--include-typedef SP_DEVINFO_DATA ^
4757
--include-struct _SP_DEVICE_INTERFACE_DATA ^
@@ -53,6 +63,9 @@ call %JEXTRACT% --source --output ../../src/main/java ^
5363
--include-macro SPDRP_ADDRESS ^
5464
--include-macro DEVPROP_TYPE_UINT32 ^
5565
--include-macro DEVPROP_TYPE_STRING ^
66+
--include-macro DEVPROP_TYPEMOD_LIST ^
67+
--include-macro DICS_FLAG_GLOBAL ^
68+
--include-macro DIREG_DEV ^
5669
windows_headers.h
5770

5871
call %JEXTRACT% --source --output ../../src/main/java ^
@@ -113,4 +126,28 @@ call %JEXTRACT% --source --output ../../src/main/java ^
113126
--include-function WinUsb_ControlTransfer ^
114127
--include-function WinUsb_WritePipe ^
115128
--include-function WinUsb_ReadPipe ^
129+
--include-function WinUsb_GetAssociatedInterface ^
130+
windows_headers.h
131+
132+
call %JEXTRACT% --source --output ../../src/main/java ^
133+
-D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^
134+
-I "%SDK_DIR%\um" ^
135+
-I "%SDK_DIR%\shared" ^
136+
-l Advapi32 ^
137+
--header-class-name Advapi32 ^
138+
--target-package net.codecrete.usb.windows.gen.advapi32 ^
139+
--include-function RegQueryValueExW ^
140+
--include-function RegCloseKey ^
141+
--include-macro REG_MULTI_SZ ^
142+
--include-macro KEY_READ ^
143+
windows_headers.h
144+
145+
call %JEXTRACT% --source --output ../../src/main/java ^
146+
-D _AMD64_ -D _M_AMD64=100 -D UNICODE -D _UNICODE ^
147+
-I "%SDK_DIR%\um" ^
148+
-I "%SDK_DIR%\shared" ^
149+
-l Ole32 ^
150+
--header-class-name Ole32 ^
151+
--target-package net.codecrete.usb.windows.gen.ole32 ^
152+
--include-function CLSIDFromString ^
116153
windows_headers.h

java-does-usb/src/main/java/net/codecrete/usb/common/USBDeviceImpl.java

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ protected USBInterfaceImpl getInterface(int interfaceNumber) {
153153
*
154154
* @param endpointNumber endpoint number (1 to 127)
155155
* @param direction transfer direction
156+
* @param transferType1 transfer type 1
157+
* @param transferType2 transfer type 2 (or {@code null})
156158
* @return endpoint address
157159
*/
158160
protected byte getEndpointAddress(int endpointNumber, USBDirection direction,
@@ -172,16 +174,69 @@ protected byte getEndpointAddress(int endpointNumber, USBDirection direction,
172174
}
173175
}
174176

177+
throwInvalidEndpointException(endpointNumber, direction, transferType1, transferType2);
178+
return 0; // will never be reached
179+
}
180+
181+
/**
182+
* Checks if the specified endpoint is valid for communication and returns the endpoint.
183+
*
184+
* @param endpointNumber endpoint number (1 to 127)
185+
* @param direction transfer direction
186+
* @param transferType1 transfer type 1
187+
* @param transferType2 transfer type 2 (or {@code null})
188+
* @return endpoint
189+
*/
190+
protected EndpointInfo getEndpoint(int endpointNumber, USBDirection direction,
191+
USBTransferType transferType1, USBTransferType transferType2) {
192+
193+
checkIsOpen();
194+
195+
if (endpointNumber >= 1 && endpointNumber <= 127) {
196+
for (var intf : interfaces_) {
197+
if (intf.isClaimed()) {
198+
for (var ep : intf.alternate().endpoints()) {
199+
if (ep.number() == endpointNumber && ep.direction() == direction
200+
&& (ep.transferType() == transferType1 || ep.transferType() == transferType2))
201+
return new EndpointInfo(intf.number(), ep.number(),
202+
(byte) (endpointNumber | (direction == USBDirection.IN ? 0x80 : 0)));
203+
}
204+
}
205+
}
206+
}
207+
208+
throwInvalidEndpointException(endpointNumber, direction, transferType1, transferType2);
209+
return null; // will never be reached
210+
}
211+
212+
protected void throwInvalidEndpointException(int endpointNumber, USBDirection direction,
213+
USBTransferType transferType1, USBTransferType transferType2) {
175214
String transferTypeDesc;
176215
if (transferType2 == null)
177216
transferTypeDesc = transferType1.name();
178217
else
179218
transferTypeDesc = String.format("%s or %s", transferType1.name(), transferType2.name());
180219
throw new USBException(String.format("Endpoint number %d does not exist, is not part of a claimed interface " +
181-
"or is not valid for %s transfer in %s direction", endpointNumber, transferTypeDesc,
220+
"or is not valid for %s transfer in %s direction", endpointNumber, transferTypeDesc,
182221
direction.name()));
183222
}
184223

224+
protected int getInterfaceNumber(int endpointNumber) {
225+
if (endpointNumber < 1 || endpointNumber > 127)
226+
return -1;
227+
228+
for (var intf : interfaces_) {
229+
if (intf.isClaimed()) {
230+
for (var ep : intf.alternate().endpoints()) {
231+
if (ep.number() == endpointNumber)
232+
return intf.number();
233+
}
234+
}
235+
}
236+
237+
return -1;
238+
}
239+
185240
@Override
186241
public abstract byte[] controlTransferIn(USBControlTransfer setup, int length);
187242

@@ -214,4 +269,7 @@ public int hashCode() {
214269
public String toString() {
215270
return "VID: 0x" + String.format("%04x", vendorId_) + ", PID: 0x" + String.format("%04x", productId_) + ", " + "manufacturer: " + manufacturer_ + ", product: " + product_ + ", serial: " + serialNumber_ + ", ID: " + id_;
216271
}
272+
273+
public record EndpointInfo(int interfaceNumber, int endpointNumber, byte endpointAddress) {
274+
}
217275
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//
2+
// Java Does USB
3+
// Copyright (c) 2022 Manuel Bleichenbacher
4+
// Licensed under MIT License
5+
// https://opensource.org/licenses/MIT
6+
//
7+
8+
package net.codecrete.usb.windows;
9+
10+
import java.lang.foreign.MemoryAddress;
11+
12+
/**
13+
* Describes the function of an interface of a composite USB device.
14+
* <p>
15+
* A composite USB device can have multiple functions, e.g. a mass
16+
* storage function and a virtual serial port function. Each function
17+
* will appear as a separate device in Window.
18+
* </p>
19+
* <p>
20+
* A function consists of one or more interfaces. Functions with
21+
* multiple interfaces must have consecutive interface numbers. The
22+
* interfaces after the first one are called associated interfaces.
23+
* </p>
24+
* <p>
25+
* On Windows, each function has a separate device path. Each device
26+
* path must be opened and each interface must be opened.
27+
* Furthermore, the first and the associated interfaces are
28+
* treated differently.
29+
* </p>
30+
*/
31+
public class CompositeFunction {
32+
// TODO: implement associated interfaces
33+
34+
// TODO: implement devices without interfaces
35+
36+
private final int firstInterfaceNumber_;
37+
private final int numInterfaces_;
38+
private final String devicePath_;
39+
private MemoryAddress deviceHandle_;
40+
private MemoryAddress firstInterfaceHandle_;
41+
42+
/**
43+
* Creates a new instance with a single interface.
44+
*
45+
* @param interfaceNumber the interface number
46+
* @param devicePath the device path
47+
*/
48+
public CompositeFunction(int interfaceNumber, String devicePath) {
49+
firstInterfaceNumber_ = interfaceNumber;
50+
numInterfaces_ = 1;
51+
devicePath_ = devicePath;
52+
}
53+
54+
public int firstInterfaceNumber() {
55+
return firstInterfaceNumber_;
56+
}
57+
58+
public int numInterfaces() {
59+
return numInterfaces_;
60+
}
61+
62+
public String devicePath() {
63+
return devicePath_;
64+
}
65+
66+
public MemoryAddress deviceHandle() {
67+
return deviceHandle_;
68+
}
69+
70+
public void setDeviceHandle(MemoryAddress deviceHandle) {
71+
deviceHandle_ = deviceHandle;
72+
}
73+
74+
public MemoryAddress firstInterfaceHandle() {
75+
return firstInterfaceHandle_;
76+
}
77+
78+
public void setFirstInterfaceHandle(MemoryAddress firstInterfaceHandle) {
79+
firstInterfaceHandle_ = firstInterfaceHandle;
80+
}
81+
}

0 commit comments

Comments
 (0)