git: 964823a18e11 - main - libusb: implement libusb_get_max_alt_packet_size

From: ShengYi Hung <aokblast_at_FreeBSD.org>
Date: Mon, 22 Jun 2026 13:37:01 UTC
The branch main has been updated by aokblast:

URL: https://cgit.FreeBSD.org/src/commit/?id=964823a18e116e86c05e1d6a6fd80b1bb3274e57

commit 964823a18e116e86c05e1d6a6fd80b1bb3274e57
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2025-07-09 10:49:24 +0000
Commit:     ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-06-22 13:36:47 +0000

    libusb: implement libusb_get_max_alt_packet_size
    
    The libusb has a function to calculate the size from given interface,
    alt_setting, endpoint. Implementing it by refactoring the calculating
    function for libusb_get_max_iso_packet_size.
    
    Reviewed by:    adrian
    Sponsored By:   FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D51225
---
 lib/libusb/libusb.3   |   6 ++
 lib/libusb/libusb.h   |   4 +-
 lib/libusb/libusb10.c | 161 +++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 141 insertions(+), 30 deletions(-)

diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3
index 7b977103eb59..d801f4c8161a 100644
--- a/lib/libusb/libusb.3
+++ b/lib/libusb/libusb.3
@@ -224,6 +224,12 @@ Returns the packet size multiplied by the packet multiplier on success,
 LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist and
 LIBUSB_ERROR_OTHERS on other failure.
 .Pp
+.Ft int
+.Fn libusb_get_max_alt_packet_size "libusb_device *dev" "int interface_number" "int alternate_setting" "unsigned char endpoint"
+Returns the maximum packet size which can be sent/received on this endpoint,
+LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist, or
+LIBUSB_ERROR_OTHER on other failure.
+.Pp
 .Ft libusb_device *
 .Fn libusb_ref_device "libusb_device *dev"
 Increment the reference counter of the device
diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h
index 00255291ebd5..b6853879412c 100644
--- a/lib/libusb/libusb.h
+++ b/lib/libusb/libusb.h
@@ -548,7 +548,9 @@ uint8_t	libusb_get_device_address(libusb_device * dev);
 enum libusb_speed libusb_get_device_speed(libusb_device * dev);
 int	libusb_clear_halt(libusb_device_handle *devh, uint8_t endpoint);
 int	libusb_get_max_packet_size(libusb_device * dev, uint8_t endpoint);
-int	libusb_get_max_iso_packet_size(libusb_device * dev, uint8_t endpoint);
+int	libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint);
+int	libusb_get_max_alt_packet_size(libusb_device *dev, int interface_number,
+    int alternate_setting, unsigned char endpoint);
 libusb_device *libusb_ref_device(libusb_device * dev);
 void	libusb_unref_device(libusb_device * dev);
 int	libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle);
diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c
index c00d8ec8be8b..ab92c219409a 100644
--- a/lib/libusb/libusb10.c
+++ b/lib/libusb/libusb10.c
@@ -536,26 +536,17 @@ libusb_get_device_speed(libusb_device *dev)
 	return (LIBUSB_SPEED_UNKNOWN);
 }
 
-int
-libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint)
+static const libusb_endpoint_descriptor *
+libusb_get_endpoint_from_config(libusb_config_descriptor *pdconf,
+    uint8_t endpoint)
 {
-	struct libusb_config_descriptor *pdconf;
 	struct libusb_interface *pinf;
 	struct libusb_interface_descriptor *pdinf;
-	struct libusb_endpoint_descriptor *pdend;
+	const struct libusb_endpoint_descriptor *pdend;
 	int i;
 	int j;
 	int k;
-	int ret;
-
-	if (dev == NULL)
-		return (LIBUSB_ERROR_NO_DEVICE);
 
-	ret = libusb_get_active_config_descriptor(dev, &pdconf);
-	if (ret < 0)
-		return (ret);
-
-	ret = LIBUSB_ERROR_NOT_FOUND;
 	for (i = 0; i < pdconf->bNumInterfaces; i++) {
 		pinf = &pdconf->interface[i];
 		for (j = 0; j < pinf->num_altsetting; j++) {
@@ -563,39 +554,151 @@ libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint)
 			for (k = 0; k < pdinf->bNumEndpoints; k++) {
 				pdend = &pdinf->endpoint[k];
 				if (pdend->bEndpointAddress == endpoint) {
-					ret = pdend->wMaxPacketSize;
-					goto out;
+					return (pdend);
 				}
 			}
 		}
 	}
 
-out:
+	return (NULL);
+}
+
+int
+libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint)
+{
+	struct libusb_config_descriptor *pdconf;
+	const struct libusb_endpoint_descriptor *pdend;
+	int ret;
+
+	if (dev == NULL)
+		return (LIBUSB_ERROR_OTHER);
+
+	ret = libusb_get_active_config_descriptor(dev, &pdconf);
+	if (ret < 0)
+		return (ret);
+
+	ret = LIBUSB_ERROR_NOT_FOUND;
+	if ((pdend = libusb_get_endpoint_from_config(pdconf, endpoint)) != NULL)
+		ret = pdend->wMaxPacketSize;
+
 	libusb_free_config_descriptor(pdconf);
 	return (ret);
 }
 
+static int
+libusb_calculate_ep_size(libusb_device *dev,
+    const libusb_endpoint_descriptor *ep)
+{
+	struct libusb_ss_endpoint_companion_descriptor *ss_ep;
+	int multiplier;
+	int speed;
+	int ret = LIBUSB_ERROR_NOT_SUPPORTED;
+	enum libusb_endpoint_transfer_type type;
+
+	speed = libusb_get_device_speed(dev);
+	if (speed >= LIBUSB_SPEED_SUPER) {
+		ret = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep,
+		    &ss_ep);
+		if (ret == LIBUSB_SUCCESS) {
+			ret = ss_ep->wBytesPerInterval;
+			libusb_free_ss_endpoint_companion_descriptor(ss_ep);
+		}
+	}
+
+	/*
+	 * reference:
+	 * https://www.keil.com/pack/doc/mw/usb/html/_u_s_b__endpoint__descriptor.html
+	 */
+	if (ret < 0) {
+		ret = ep->wMaxPacketSize;
+		switch (speed) {
+		case LIBUSB_SPEED_LOW:
+		case LIBUSB_SPEED_FULL:
+			break;
+		default:
+			type = ep->bmAttributes & 0x3;
+			if (type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS ||
+			    type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) {
+				multiplier = (1 + ((ret >> 11) & 3));
+				if (multiplier > 3)
+					multiplier = 3;
+				ret = (ret & 0x7FF) * multiplier;
+			}
+			break;
+		}
+	}
+
+	return (ret);
+}
+
 int
 libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint)
 {
-	int multiplier;
+	struct libusb_config_descriptor *pdconf;
+	const struct libusb_endpoint_descriptor *pdend;
 	int ret;
 
-	ret = libusb_get_max_packet_size(dev, endpoint);
+	if (dev == NULL)
+		return (LIBUSB_ERROR_OTHER);
 
-	switch (libusb20_dev_get_speed(dev->os_priv)) {
-	case LIBUSB20_SPEED_LOW:
-	case LIBUSB20_SPEED_FULL:
-		break;
-	default:
-		if (ret > -1) {
-			multiplier = (1 + ((ret >> 11) & 3));
-			if (multiplier > 3)
-				multiplier = 3;
-			ret = (ret & 0x7FF) * multiplier;
+	ret = libusb_get_active_config_descriptor(dev, &pdconf);
+	if (ret < 0)
+		return (LIBUSB_ERROR_OTHER);
+
+	pdend = libusb_get_endpoint_from_config(pdconf, endpoint);
+	if (pdend == NULL) {
+		libusb_free_config_descriptor(pdconf);
+		return (LIBUSB_ERROR_NOT_FOUND);
+	}
+
+	ret = libusb_calculate_ep_size(dev, pdend);
+	libusb_free_config_descriptor(pdconf);
+	return (ret);
+}
+
+int
+libusb_get_max_alt_packet_size(libusb_device *dev, int interface_number,
+    int alternate_setting, unsigned char endpoint)
+{
+	struct libusb_config_descriptor *pdconf;
+	struct libusb_interface *pinf;
+	struct libusb_interface_descriptor *pdinf;
+	const struct libusb_endpoint_descriptor *pdend = NULL;
+	const struct libusb_endpoint_descriptor *pdend_tmp;
+	int ret, i;
+
+	if (dev == NULL)
+		return (LIBUSB_ERROR_OTHER);
+
+	ret = libusb_get_active_config_descriptor(dev, &pdconf);
+	if (ret < 0)
+		return (LIBUSB_ERROR_OTHER);
+
+	if (pdconf->bNumInterfaces <= interface_number) {
+		libusb_free_config_descriptor(pdconf);
+		return (LIBUSB_ERROR_NOT_FOUND);
+	}
+	pinf = &pdconf->interface[interface_number];
+
+	if (pinf->num_altsetting <= alternate_setting) {
+		libusb_free_config_descriptor(pdconf);
+		return (LIBUSB_ERROR_NOT_FOUND);
+	}
+	pdinf = &pinf->altsetting[alternate_setting];
+
+	for (i = 0; i < pdinf->bNumEndpoints; ++i) {
+		pdend_tmp = &pdinf->endpoint[i];
+		if (pdend_tmp->bEndpointAddress == endpoint) {
+			pdend = pdend_tmp;
+			break;
 		}
-		break;
 	}
+	if (pdend != NULL)
+		ret = libusb_calculate_ep_size(dev, pdend);
+	else
+		ret = LIBUSB_ERROR_NOT_FOUND;
+	libusb_free_config_descriptor(pdconf);
+
 	return (ret);
 }