[PATCH v2 02/21] USB: core: add helpers to retrieve endpoints in reverse order

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Several drivers have implemented their endpoint look-up loops in such a
way that they have picked the last endpoint descriptor of the specified
type should more than one such descriptor exist.

To avoid any regressions, add corresponding helpers to lookup endpoints
by searching the endpoint descriptors in reverse order.

Signed-off-by: Johan Hovold <johan@xxxxxxxxxx>
---
 drivers/usb/core/usb.c | 112 +++++++++++++++++++++++++++++++++----------------
 include/linux/usb.h    |  35 ++++++++++++++++
 2 files changed, 111 insertions(+), 36 deletions(-)

diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 235438919671..feb8b6b9e5eb 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -74,6 +74,48 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
 #define usb_autosuspend_delay		0
 #endif
 
+static bool match_endpoint(struct usb_endpoint_descriptor *epd,
+		struct usb_endpoint_descriptor **bulk_in,
+		struct usb_endpoint_descriptor **bulk_out,
+		struct usb_endpoint_descriptor **int_in,
+		struct usb_endpoint_descriptor **int_out)
+{
+	switch (usb_endpoint_type(epd)) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (usb_endpoint_dir_in(epd)) {
+			if (bulk_in && !*bulk_in) {
+				*bulk_in = epd;
+				break;
+			}
+		} else {
+			if (bulk_out && !*bulk_out) {
+				*bulk_out = epd;
+				break;
+			}
+		}
+
+		return false;
+	case USB_ENDPOINT_XFER_INT:
+		if (usb_endpoint_dir_in(epd)) {
+			if (int_in && !*int_in) {
+				*int_in = epd;
+				break;
+			}
+		} else {
+			if (int_out && !*int_out) {
+				*int_out = epd;
+				break;
+			}
+		}
+
+		return false;
+	default:
+		return false;
+	}
+
+	return (!bulk_in || *bulk_in) && (!bulk_out || *bulk_out) &&
+			(!int_in || *int_in) && (!int_out || *int_out);
+}
 
 /**
  * usb_find_common_endpoints() -- look up common endpoint descriptors
@@ -113,50 +155,48 @@ int usb_find_common_endpoints(struct usb_host_interface *alt,
 	for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
 		epd = &alt->endpoint[i].desc;
 
-		switch (usb_endpoint_type(epd)) {
-		case USB_ENDPOINT_XFER_BULK:
-			if (usb_endpoint_dir_in(epd)) {
-				if (bulk_in && !*bulk_in) {
-					*bulk_in = epd;
-					break;
-				}
-			} else {
-				if (bulk_out && !*bulk_out) {
-					*bulk_out = epd;
-					break;
-				}
-			}
+		if (match_endpoint(epd, bulk_in, bulk_out, int_in, int_out))
+			return 0;
+	}
 
-			continue;
-		case USB_ENDPOINT_XFER_INT:
-			if (usb_endpoint_dir_in(epd)) {
-				if (int_in && !*int_in) {
-					*int_in = epd;
-					break;
-				}
-			} else {
-				if (int_out && !*int_out) {
-					*int_out = epd;
-					break;
-				}
-			}
+	return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(usb_find_common_endpoints);
 
-			continue;
-		default:
-			continue;
-		}
+/**
+ * usb_find_common_endpoints_reverse() -- look up common endpoint descriptors
+ *
+ * Same as usb_find_common_endpoints(), but the endpoint descriptors are
+ * searched in reverse order (see usb_find_common_endpoints() for details).
+ */
+int usb_find_common_endpoints_reverse(struct usb_host_interface *alt,
+		struct usb_endpoint_descriptor **bulk_in,
+		struct usb_endpoint_descriptor **bulk_out,
+		struct usb_endpoint_descriptor **int_in,
+		struct usb_endpoint_descriptor **int_out)
+{
+	struct usb_endpoint_descriptor *epd;
+	int i;
+
+	if (bulk_in)
+		*bulk_in = NULL;
+	if (bulk_out)
+		*bulk_out = NULL;
+	if (int_in)
+		*int_in = NULL;
+	if (int_out)
+		*int_out = NULL;
+
+	for (i = alt->desc.bNumEndpoints - 1; i >= 0; --i) {
+		epd = &alt->endpoint[i].desc;
 
-		if ((!bulk_in || *bulk_in) &&
-				(!bulk_out || *bulk_out) &&
-				(!int_in || *int_in) &&
-				(!int_out || *int_out)) {
+		if (match_endpoint(epd, bulk_in, bulk_out, int_in, int_out))
 			return 0;
-		}
 	}
 
 	return -ENXIO;
 }
-EXPORT_SYMBOL_GPL(usb_find_common_endpoints);
+EXPORT_SYMBOL_GPL(usb_find_common_endpoints_reverse);
 
 /**
  * usb_find_alt_setting() - Given a configuration, find the alternate setting
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 82e7d52b4ad9..82d0456e6dea 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -106,6 +106,13 @@ usb_find_common_endpoints(struct usb_host_interface *alt,
 		struct usb_endpoint_descriptor **int_in,
 		struct usb_endpoint_descriptor **int_out);
 
+int __must_check
+usb_find_common_endpoints_reverse(struct usb_host_interface *alt,
+		struct usb_endpoint_descriptor **bulk_in,
+		struct usb_endpoint_descriptor **bulk_out,
+		struct usb_endpoint_descriptor **int_in,
+		struct usb_endpoint_descriptor **int_out);
+
 static inline int __must_check
 usb_find_bulk_in_endpoint(struct usb_host_interface *alt,
 		struct usb_endpoint_descriptor **bulk_in)
@@ -134,6 +141,34 @@ usb_find_int_out_endpoint(struct usb_host_interface *alt,
 	return usb_find_common_endpoints(alt, NULL, NULL, NULL, int_out);
 }
 
+static inline int __must_check
+usb_find_last_bulk_in_endpoint(struct usb_host_interface *alt,
+		struct usb_endpoint_descriptor **bulk_in)
+{
+	return usb_find_common_endpoints_reverse(alt, bulk_in, NULL, NULL, NULL);
+}
+
+static inline int __must_check
+usb_find_last_bulk_out_endpoint(struct usb_host_interface *alt,
+		struct usb_endpoint_descriptor **bulk_out)
+{
+	return usb_find_common_endpoints_reverse(alt, NULL, bulk_out, NULL, NULL);
+}
+
+static inline int __must_check
+usb_find_last_int_in_endpoint(struct usb_host_interface *alt,
+		struct usb_endpoint_descriptor **int_in)
+{
+	return usb_find_common_endpoints_reverse(alt, NULL, NULL, int_in, NULL);
+}
+
+static inline int __must_check
+usb_find_last_int_out_endpoint(struct usb_host_interface *alt,
+		struct usb_endpoint_descriptor **int_out)
+{
+	return usb_find_common_endpoints_reverse(alt, NULL, NULL, NULL, int_out);
+}
+
 /**
  * struct usb_interface - what usb device drivers talk to
  * @altsetting: array of interface structures, one for each alternate
-- 
2.12.0

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux