Skip to content

Commit

Permalink
Add support for proxying isochronous OUT endpoints
Browse files Browse the repository at this point in the history
With this change, I'm able to proxy one of Logitech USB web cameras at a
low resolution (160x120 pixels) via dwc3.

Proxying with higher resolutions fails to produce a picture on the host:
I think because the transfer rate is too low and all the frames get dropped.
  • Loading branch information
xairy committed Jun 15, 2024
1 parent 4a48d46 commit 5bef5cb
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 2 deletions.
37 changes: 35 additions & 2 deletions device-libusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,18 +299,51 @@ int send_data(uint8_t endpoint, uint8_t attributes, uint8_t *dataptr,
return result;
}

void iso_transfer_callback(struct libusb_transfer *transfer) {
int *iso_completed = (int *)transfer->user_data;
*iso_completed = 1;
}

int receive_data(uint8_t endpoint, uint8_t attributes, uint16_t maxPacketSize,
uint8_t **dataptr, int *length, int timeout) {
int result = LIBUSB_SUCCESS;
struct libusb_transfer *transfer;
int iso_completed, iso_packets;

int attempt = 0;
switch (attributes & USB_ENDPOINT_XFERTYPE_MASK) {
case USB_ENDPOINT_XFER_CONTROL:
fprintf(stderr, "Can't read on a control endpoint.\n");
break;
case USB_ENDPOINT_XFER_ISOC:
if (verbose_level)
fprintf(stderr, "Isochronous(read) endpoint EP%02x unhandled.\n", endpoint);
*dataptr = new uint8_t[maxPacketSize];
// We could retrieve multiple packets at a time, but then we
// would need to split the received data submit each packet
// separately via Raw Gadget. So retrieve only one packet for
// simplicity.
iso_packets = 1;
transfer = libusb_alloc_transfer(iso_packets);
if (!transfer) {
fprintf(stderr, "Failed to allocate libusb_transfer.\n");
result = LIBUSB_ERROR_OTHER;
}
iso_completed = 0;
libusb_fill_iso_transfer(transfer, dev_handle, endpoint, *dataptr, maxPacketSize,
iso_packets, iso_transfer_callback, &iso_completed, timeout);
libusb_set_iso_packet_lengths(transfer, maxPacketSize / iso_packets);
result = libusb_submit_transfer(transfer);
if (result != LIBUSB_SUCCESS) {
libusb_free_transfer(transfer);
break;
}
while (!iso_completed)
libusb_handle_events_completed(NULL, &iso_completed);
*length = 0;
for (int i = 0; i < iso_packets; i++)
*length += transfer->iso_packet_desc[i].actual_length;
if (result == LIBUSB_SUCCESS && verbose_level > 2)
printf("Received iso data(%d) bytes\n", *length);
libusb_free_transfer(transfer);
break;
case USB_ENDPOINT_XFER_BULK:
*dataptr = new uint8_t[maxPacketSize * 8];
Expand Down
5 changes: 5 additions & 0 deletions host-raw-gadget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ int usb_raw_ep_write(int fd, struct usb_raw_ep_io *io) {
// endpoint threads when shutting them down.
return rv;
}
else if (errno == EXDEV) {
// Ignore failures caused by sending an isochronous
// transfer too late (dwc3 returns EXDEV).
return rv;
}
else if (errno == EBUSY)
return rv;
perror("ioctl(USB_RAW_IOCTL_EP_WRITE)");
Expand Down
5 changes: 5 additions & 0 deletions proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ void *ep_loop_write(void *arg) {
ep.bEndpointAddress, transfer_type.c_str(), dir.c_str());
break;
}
if (rv < 0 && errno == EXDEV) {
printf("EP%x(%s_%s): missed isochronous timing, ignoring transfer\n",
ep.bEndpointAddress, transfer_type.c_str(), dir.c_str());
continue;
}
else if (rv < 0) {
perror("usb_raw_ep_write()");
exit(EXIT_FAILURE);
Expand Down

0 comments on commit 5bef5cb

Please sign in to comment.