[efi] Adjust packet length based on requested size

When host sends less than USB request length amount of data, and the
data size is multiple of the endpoint max packet size, the driver will
hang waiting for more data. To prevent this we need to adjust the
request length based on the actual requested receive size.

Change-Id: I6e1a8041e2f8979c5d9c37b1f05f7b1d859e46fc
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/867630
Reviewed-by: David Pursell <dpursell@google.com>
GitOrigin-RevId: 5c7d133bcc3b027677c96f91882697bd0c75b9d0
diff --git a/lib/efi_loader/efi_android_boot.c b/lib/efi_loader/efi_android_boot.c
index 5ef7fb6..6e994cc 100644
--- a/lib/efi_loader/efi_android_boot.c
+++ b/lib/efi_loader/efi_android_boot.c
@@ -20,6 +20,7 @@
 static struct android_boot_protocol_instance {
 	efi_android_boot_protocol protocol;
 	struct efi_event *timer_event;
+	size_t max_out_req_length;
 	bool send_completion_already_signaled;
 } android_boot;
 
@@ -98,22 +99,44 @@
 
 	if (!fastboot_func->out_req ||
 	    fastboot_func->out_req->status == -EINPROGRESS) {
+		// Not initialized or the out request packet is queued and busy.
 		return EFI_EXIT(EFI_NOT_READY);
 	} else if (fastboot_func->out_req->status != 0) {
+		// The out request has encountered an error
 		return EFI_EXIT(EFI_DEVICE_ERROR);
-	} else if (*buffer_size < (size_t)fastboot_func->out_req->actual) {
+	} else if (fastboot_func->out_req->actual) {
+		// Has available data.
+		if (*buffer_size < (size_t)fastboot_func->out_req->actual) {
+			// We don't support partial read, the caller has to read the whole
+			// packet it previously requested.
+			*buffer_size = (size_t)fastboot_func->out_req->actual;
+			return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+		}
+		memcpy(buffer, fastboot_func->out_req->buf,
+		       (size_t)fastboot_func->out_req->actual);
 		*buffer_size = (size_t)fastboot_func->out_req->actual;
-		return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+		// Clear the `actual` field to indicate idleness.
+		fastboot_func->out_req->actual = 0;
+		return EFI_EXIT(EFI_SUCCESS);
+	} else {
+		// Usb receive request is not queued yet. Queue it.
+		// Adjusts the request length to be the smaller between the maximum
+		// length and the requested read size, round up to the multiple of max
+		// packet size. This is necessary otherwise if host sends smaller than
+		// length amount of data, and it is a multiple of max packet size,
+		// driver hangs.
+		size_t length =
+			min(*buffer_size, android_boot.max_out_req_length);
+		length = (length + (fastboot_func->out_ep->maxpacket - 1)) /
+			 fastboot_func->out_ep->maxpacket *
+			 fastboot_func->out_ep->maxpacket;
+		fastboot_func->out_req->length = length;
+		if (usb_ep_queue(fastboot_func->out_ep, fastboot_func->out_req,
+				 0)) {
+			return EFI_EXIT(EFI_DEVICE_ERROR);
+		}
+		return EFI_EXIT(EFI_NOT_READY);
 	}
-
-	memcpy(buffer, fastboot_func->out_req->buf,
-	       (size_t)fastboot_func->out_req->actual);
-	*buffer_size = (size_t)fastboot_func->out_req->actual;
-	// Queues back the USB rx request to receive the next packet.
-	if (usb_ep_queue(fastboot_func->out_ep, fastboot_func->out_req, 0)) {
-		return EFI_EXIT(EFI_DEVICE_ERROR);
-	}
-	return EFI_EXIT(EFI_SUCCESS);
 }
 
 static efi_status_t EFIAPI
@@ -228,5 +251,6 @@
 		return ret;
 	}
 
+	android_boot.max_out_req_length = fastboot_func->out_req->length;
 	return EFI_SUCCESS;
 }