Skip to content

Commit 8e8a5d8

Browse files
committed
Control transfer use async IO internally (Linux)
1 parent 9c2dcfd commit 8e8a5d8

File tree

10 files changed

+115
-57
lines changed

10 files changed

+115
-57
lines changed

java-does-usb/jextract/linux/gen_linux.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ $JEXTRACT --source --output ../../src/main/java \
6060
--include-constant USBDEVFS_SUBMITURB \
6161
--include-constant USBDEVFS_DISCARDURB \
6262
--include-constant USBDEVFS_REAPURB \
63+
--include-constant USBDEVFS_URB_TYPE_INTERRUPT \
64+
--include-constant USBDEVFS_URB_TYPE_CONTROL \
6365
--include-constant USBDEVFS_URB_TYPE_BULK \
66+
--include-constant USBDEVFS_URB_TYPE_ISO \
6467
/usr/include/linux/usbdevice_fs.h
6568

6669
# libudev.h

java-does-usb/src/main/java/net/codecrete/usb/USBTransferType.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
* USB endpoint transfer type enumeration.
1212
*/
1313
public enum USBTransferType {
14+
/**
15+
* Control transfer
16+
*/
17+
CONTROL,
1418
/**
1519
* Bulk transfer
1620
*/

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ protected EndpointInfo getEndpoint(USBDirection direction, int endpointNumber, U
249249
if (ep.number() == endpointNumber && ep.direction() == direction && (ep.transferType() == transferType1 || ep.transferType() == transferType2))
250250
return new EndpointInfo(intf.number(), ep.number(),
251251
(byte) (endpointNumber | (direction == USBDirection.IN ? 0x80 : 0)),
252-
ep.packetSize());
252+
ep.packetSize(), ep.transferType());
253253
}
254254
}
255255
}
@@ -418,6 +418,7 @@ public String toString() {
418418
return "VID: 0x" + String.format("%04x", vendorId_) + ", PID: 0x" + String.format("%04x", productId_) + ", " + "manufacturer: " + manufacturer_ + ", product: " + product_ + ", serial: " + serialNumber_ + ", ID: " + id_;
419419
}
420420

421-
public record EndpointInfo(int interfaceNumber, int endpointNumber, byte endpointAddress, int packetSize) {
421+
public record EndpointInfo(int interfaceNumber, int endpointNumber, byte endpointAddress, int packetSize,
422+
USBTransferType transferType) {
422423
}
423424
}

java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxAsyncTask.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package net.codecrete.usb.linux;
99

10+
import net.codecrete.usb.USBTransferType;
1011
import net.codecrete.usb.linux.gen.errno.errno;
1112
import net.codecrete.usb.linux.gen.poll.poll;
1213
import net.codecrete.usb.linux.gen.poll.pollfd;
@@ -26,7 +27,7 @@
2627
import static net.codecrete.usb.linux.LinuxUSBException.throwException;
2728
import static net.codecrete.usb.linux.LinuxUSBException.throwLastError;
2829
import static net.codecrete.usb.linux.USBDevFS.*;
29-
import static net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs.USBDEVFS_URB_TYPE_BULK;
30+
import static net.codecrete.usb.linux.gen.usbdevice_fs.usbdevice_fs.*;
3031

3132
/**
3233
* Background task for handling asynchronous transfers.
@@ -35,7 +36,7 @@
3536
* </p>
3637
* <p>
3738
* The task keeps track of the submitted transfers by remembering the
38-
* URB address (USB request buffer) in order to match it to the
39+
* URB address (USB request block) in order to match it to the
3940
* completion.
4041
* </p>
4142
* <p>
@@ -232,12 +233,12 @@ private synchronized void removeFdFromAsyncIOCompletion(int fd) {
232233
asyncFds = fds;
233234
}
234235

235-
synchronized void submitBulkTransfer(LinuxUSBDevice device, int endpointAddress, LinuxTransfer transfer) {
236+
synchronized void submitTransfer(LinuxUSBDevice device, int endpointAddress, USBTransferType transferType, LinuxTransfer transfer) {
236237

237238
addURB(transfer);
238239
var urb = transfer.urb;
239240

240-
usbdevfs_urb.type$set(urb, (byte) USBDEVFS_URB_TYPE_BULK());
241+
usbdevfs_urb.type$set(urb, (byte) urbTransferType(transferType));
241242
usbdevfs_urb.endpoint$set(urb, (byte) endpointAddress);
242243
usbdevfs_urb.buffer$set(urb, transfer.data);
243244
usbdevfs_urb.buffer_length$set(urb, transfer.dataSize);
@@ -247,11 +248,21 @@ synchronized void submitBulkTransfer(LinuxUSBDevice device, int endpointAddress,
247248
var errnoState = arena.allocate(Linux.ERRNO_STATE.layout());
248249
if (IO.ioctl(device.fileDescriptor(), SUBMITURB, urb, errnoState) < 0) {
249250
String action = endpointAddress >= 128 ? "reading from" : "writing to";
250-
throwLastError(errnoState, "failed %s endpoint %d", action, endpointAddress);
251+
String endpoint = endpointAddress == 0 ? "control endpoint" : String.format("endpoint %d", endpointAddress);
252+
throwLastError(errnoState, "failed %s %s", action, endpoint);
251253
}
252254
}
253255
}
254256

257+
private static int urbTransferType(USBTransferType transferType) {
258+
return switch (transferType) {
259+
case BULK -> USBDEVFS_URB_TYPE_BULK();
260+
case INTERRUPT -> USBDEVFS_URB_TYPE_INTERRUPT();
261+
case CONTROL -> USBDEVFS_URB_TYPE_CONTROL();
262+
case ISOCHRONOUS -> USBDEVFS_URB_TYPE_ISO();
263+
};
264+
}
265+
255266
private void addURB(LinuxTransfer transfer) {
256267
MemorySegment urb;
257268
int size = availableURBs.size();

java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointInputStream.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ public class LinuxEndpointInputStream extends EndpointInputStream {
1919

2020
@Override
2121
protected void submitTransferIn(Transfer transfer) {
22-
((LinuxUSBDevice) device).submitBulkTransfer(USBDirection.IN, endpointNumber, (LinuxTransfer) transfer);
22+
((LinuxUSBDevice) device).submitTransfer(USBDirection.IN, endpointNumber, (LinuxTransfer) transfer);
2323
}
2424
}

java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxEndpointOutputStream.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ public class LinuxEndpointOutputStream extends EndpointOutputStream {
1919

2020
@Override
2121
protected void submitTransferOut(Transfer transfer) {
22-
((LinuxUSBDevice) device).submitBulkTransfer(USBDirection.OUT, endpointNumber, (LinuxTransfer) transfer);
22+
((LinuxUSBDevice) device).submitTransfer(USBDirection.OUT, endpointNumber, (LinuxTransfer) transfer);
2323
}
2424
}

java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxTransfer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
public class LinuxTransfer extends Transfer {
1616
/**
17-
* USB request buffer.
17+
* USB request block.
1818
*
1919
* @see usbdevfs_urb
2020
*/

java-does-usb/src/main/java/net/codecrete/usb/linux/LinuxUSBDevice.java

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
import net.codecrete.usb.common.USBInterfaceImpl;
1717
import net.codecrete.usb.linux.gen.fcntl.fcntl;
1818
import net.codecrete.usb.linux.gen.unistd.unistd;
19-
import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_ctrltransfer;
2019
import net.codecrete.usb.linux.gen.usbdevice_fs.usbdevfs_setinterface;
2120
import net.codecrete.usb.usbstandard.DeviceDescriptor;
21+
import net.codecrete.usb.usbstandard.SetupPacket;
2222

2323
import java.io.IOException;
2424
import java.io.InputStream;
@@ -165,71 +165,74 @@ public synchronized void releaseInterface(int interfaceNumber) {
165165
}
166166
}
167167

168-
private MemorySegment createCtrlTransfer(Arena arena, USBDirection direction, USBControlTransfer setup,
169-
MemorySegment data) {
170-
var ctrlTransfer = arena.allocate(usbdevfs_ctrltransfer.$LAYOUT());
171-
var bmRequest =
172-
(direction == USBDirection.IN ? 0x80 : 0) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal();
173-
usbdevfs_ctrltransfer.bRequestType$set(ctrlTransfer, (byte) bmRequest);
174-
usbdevfs_ctrltransfer.bRequest$set(ctrlTransfer, (byte) setup.request());
175-
usbdevfs_ctrltransfer.wValue$set(ctrlTransfer, (short) setup.value());
176-
usbdevfs_ctrltransfer.wIndex$set(ctrlTransfer, (short) setup.index());
177-
usbdevfs_ctrltransfer.wLength$set(ctrlTransfer, (short) data.byteSize());
178-
usbdevfs_ctrltransfer.data$set(ctrlTransfer, data);
179-
return ctrlTransfer;
180-
}
181-
182168
@Override
183-
public byte[] controlTransferIn(USBControlTransfer setup, int length) {
169+
public void controlTransferOut(USBControlTransfer setup, byte[] data) {
184170
try (var arena = Arena.openConfined()) {
185-
var data = arena.allocate(length);
186-
var ctrlTransfer = createCtrlTransfer(arena, USBDirection.IN, setup, data);
187-
188-
var errnoState = arena.allocate(Linux.ERRNO_STATE.layout());
189-
int res = IO.ioctl(fd, USBDevFS.CONTROL, ctrlTransfer, errnoState);
190-
if (res < 0)
191-
throwLastError(errnoState, "Control IN transfer failed");
171+
int dataLength = data != null ? data.length : 0;
172+
var transfer = createSyncCtrlTransfer(arena, USBDirection.OUT, setup, dataLength);
173+
if (dataLength != 0)
174+
transfer.data.asSlice(8).copyFrom(MemorySegment.ofArray(data));
192175

193-
return data.asSlice(0, res).toArray(JAVA_BYTE);
176+
synchronized (transfer) {
177+
submitTransfer(USBDirection.OUT, 0, transfer);
178+
waitForTransfer(transfer, 0, USBDirection.OUT, 0);
179+
}
194180
}
195181
}
196182

197183
@Override
198-
public void controlTransferOut(USBControlTransfer setup, byte[] data) {
184+
public byte[] controlTransferIn(USBControlTransfer setup, int length) {
199185
try (var arena = Arena.openConfined()) {
200-
int dataLength = data != null ? data.length : 0;
201-
var buffer = arena.allocate(dataLength);
202-
if (dataLength != 0)
203-
buffer.copyFrom(MemorySegment.ofArray(data));
204-
var ctrlTransfer = createCtrlTransfer(arena, USBDirection.OUT, setup, buffer);
186+
var transfer = createSyncCtrlTransfer(arena, USBDirection.IN, setup, length);
205187

206-
var errnoState = arena.allocate(Linux.ERRNO_STATE.layout());
207-
int res = IO.ioctl(fd, USBDevFS.CONTROL, ctrlTransfer, errnoState);
208-
if (res < 0)
209-
throwLastError(errnoState, "Control OUT transfer failed");
188+
synchronized (transfer) {
189+
submitTransfer(USBDirection.IN, 0, transfer);
190+
waitForTransfer(transfer, 0, USBDirection.IN, 0);
191+
}
192+
193+
return transfer.data.asSlice(8, transfer.resultSize).toArray(JAVA_BYTE);
210194
}
211195
}
212196

213-
private LinuxTransfer createSyncTransfer(MemorySegment data) {
197+
/**
198+
* Create transfer object for synchronous control request.
199+
*
200+
* @param arena arena for allocating memory
201+
* @param direction direction
202+
* @param setup setup data
203+
* @param dataLength data length (in addition to setup data)
204+
* @return transfer object
205+
*/
206+
private LinuxTransfer createSyncCtrlTransfer(Arena arena, USBDirection direction, USBControlTransfer setup,
207+
int dataLength) {
208+
var bmRequest =
209+
(direction == USBDirection.IN ? 0x80 : 0) | (setup.requestType().ordinal() << 5) | setup.recipient().ordinal();
210+
var buffer = arena.allocate(8 + dataLength, 8);
211+
var setupPacket = new SetupPacket(buffer);
212+
setupPacket.setRequestType(bmRequest);
213+
setupPacket.setRequest(setup.request());
214+
setupPacket.setValue(setup.value());
215+
setupPacket.setIndex(setup.index());
216+
setupPacket.setLength(dataLength);
217+
214218
var transfer = new LinuxTransfer();
215-
transfer.data = data;
216-
transfer.dataSize = (int) data.byteSize();
219+
transfer.data = buffer;
220+
transfer.dataSize = (int) buffer.byteSize();
217221
transfer.resultSize = -1;
218222
transfer.completion = USBDeviceImpl::onSyncTransferCompleted;
223+
219224
return transfer;
220225
}
221226

222227
@Override
223228
public void transferOut(int endpointNumber, byte[] data, int timeout) {
224-
var endpoint = getEndpoint(USBDirection.OUT, endpointNumber, USBTransferType.BULK, USBTransferType.INTERRUPT);
225-
226229
try (var arena = Arena.openConfined()) {
227230
var buffer = arena.allocate(data.length);
228231
buffer.copyFrom(MemorySegment.ofArray(data));
229232
var transfer = createSyncTransfer(buffer);
230233

231234
synchronized (transfer) {
232-
asyncTask.submitBulkTransfer(this, endpoint.endpointAddress(), transfer);
235+
submitTransfer(USBDirection.OUT, endpointNumber, transfer);
233236
waitForTransfer(transfer, timeout, USBDirection.OUT, endpointNumber);
234237
}
235238
}
@@ -244,14 +247,32 @@ public byte[] transferIn(int endpointNumber, int timeout) {
244247
var transfer = createSyncTransfer(buffer);
245248

246249
synchronized (transfer) {
247-
asyncTask.submitBulkTransfer(this, endpoint.endpointAddress(), transfer);
250+
submitTransfer(USBDirection.IN, endpointNumber, transfer);
248251
waitForTransfer(transfer, timeout, USBDirection.IN, endpointNumber);
249252
}
250253

251254
return buffer.asSlice(0, transfer.resultSize).toArray(JAVA_BYTE);
252255
}
253256
}
254257

258+
private LinuxTransfer createSyncTransfer(MemorySegment data) {
259+
var transfer = new LinuxTransfer();
260+
transfer.data = data;
261+
transfer.dataSize = (int) data.byteSize();
262+
transfer.resultSize = -1;
263+
transfer.completion = USBDeviceImpl::onSyncTransferCompleted;
264+
return transfer;
265+
}
266+
267+
synchronized void submitTransfer(USBDirection direction, int endpointNumber, LinuxTransfer transfer) {
268+
if (endpointNumber != 0) {
269+
var endpoint = getEndpoint(direction, endpointNumber, USBTransferType.BULK, USBTransferType.INTERRUPT);
270+
asyncTask.submitTransfer(this, endpoint.endpointAddress(), endpoint.transferType(), transfer);
271+
} else {
272+
asyncTask.submitTransfer(this, 0, USBTransferType.CONTROL, transfer);
273+
}
274+
}
275+
255276
@Override
256277
protected Transfer createTransfer() {
257278
return new LinuxTransfer();
@@ -297,9 +318,4 @@ public OutputStream openOutputStream(int endpointNumber) {
297318

298319
return new LinuxEndpointOutputStream(this, endpointNumber);
299320
}
300-
301-
synchronized void submitBulkTransfer(USBDirection direction, int endpointNumber, LinuxTransfer transfer) {
302-
var endpoint = getEndpoint(direction, endpointNumber, USBTransferType.BULK, null);
303-
asyncTask.submitBulkTransfer(this, endpoint.endpointAddress(), transfer);
304-
}
305321
}

java-does-usb/src/main/java/net/codecrete/usb/linux/gen/usbdevice_fs/usbdevice_fs.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ public class usbdevice_fs {
1313
public static final OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT;
1414
public static final OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT;
1515
public static final OfAddress C_POINTER = Constants$root.C_POINTER$LAYOUT;
16+
/**
17+
* {@snippet :
18+
* #define USBDEVFS_URB_TYPE_ISO 0
19+
* }
20+
*/
21+
public static int USBDEVFS_URB_TYPE_ISO() {
22+
return (int)0L;
23+
}
24+
/**
25+
* {@snippet :
26+
* #define USBDEVFS_URB_TYPE_INTERRUPT 1
27+
* }
28+
*/
29+
public static int USBDEVFS_URB_TYPE_INTERRUPT() {
30+
return (int)1L;
31+
}
32+
/**
33+
* {@snippet :
34+
* #define USBDEVFS_URB_TYPE_CONTROL 2
35+
* }
36+
*/
37+
public static int USBDEVFS_URB_TYPE_CONTROL() {
38+
return (int)2L;
39+
}
1640
/**
1741
* {@snippet :
1842
* #define USBDEVFS_URB_TYPE_BULK 3

reference/linux/usb_registry.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ usb_registry::usb_registry()
3131
: monitor_wake_event_fd(-1),
3232
on_connected_callback(nullptr), on_disconnected_callback(nullptr), is_device_list_ready(false),
3333
async_io_update_event_fd(-1), async_io_update_request(0), async_io_update_response(0) {
34-
std::cout << std::hex << USBDEVFS_REAPURBNDELAY << std::endl;
3534
}
3635

3736
usb_registry::~usb_registry() {

0 commit comments

Comments
 (0)