libpayload: usbmsc: Factor out usb_msc_force_init() function

We're planning to have a use case with a custom USB device that
implements the USB mass storage protocol on its bulk endpoints, but does
not have the normal MSC class/protocol interface descriptors and does
not support class-specific control requests (Get Max LUN and Bulk-Only
Reset). We'd like to identify/enumerate the device via
usb_generic_create() in our payload but then reuse all the normal MSC
driver code. In order to make that possible, this patch factors a new
usb_msc_force_init() function out of usb_msc_init() which will
initialize an MSC device without checking its descriptors. It also adds
some "quirks" flags that allow devices registered this way to customize
behavior of the MSC stack.

Change-Id: I50392128409cb2a879954f234149a5e3b060a229
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/34227
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
diff --git a/payloads/libpayload/drivers/usb/usbmsc.c b/payloads/libpayload/drivers/usb/usbmsc.c
index d793e34..2412e99 100644
--- a/payloads/libpayload/drivers/usb/usbmsc.c
+++ b/payloads/libpayload/drivers/usb/usbmsc.c
@@ -157,6 +157,9 @@
 	dr.wIndex = 0;
 	dr.wLength = 0;
 
+	if (MSC_INST (dev)->quirks & USB_MSC_QUIRK_NO_RESET)
+		return MSC_COMMAND_FAIL;
+
 	/* if any of these fails, detach device, as we are lost */
 	if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0) < 0 ||
 			clear_stall (MSC_INST (dev)->bulk_in) ||
@@ -185,7 +188,8 @@
 	dr.wValue = 0;
 	dr.wIndex = 0;
 	dr.wLength = 1;
-	if (dev->controller->control (dev, IN, sizeof (dr), &dr,
+	if (MSC_INST (dev)->quirks & USB_MSC_QUIRK_NO_LUNS ||
+	    dev->controller->control (dev, IN, sizeof (dr), &dr,
 			sizeof (msc->num_luns), &msc->num_luns) < 0)
 		msc->num_luns = 0;	/* assume only 1 lun if req fails */
 	msc->num_luns++;	/* Get Max LUN returns number of last LUN */
@@ -600,14 +604,6 @@
 void
 usb_msc_init (usbdev_t *dev)
 {
-	int i;
-
-	/* init .data before setting .destroy */
-	dev->data = NULL;
-
-	dev->destroy = usb_msc_destroy;
-	dev->poll = usb_msc_poll;
-
 	configuration_descriptor_t *cd =
 		(configuration_descriptor_t *) dev->configuration;
 	interface_descriptor_t *interface =
@@ -634,6 +630,19 @@
 		return;
 	}
 
+	usb_msc_force_init (dev, 0);
+}
+
+void usb_msc_force_init (usbdev_t *dev, u32 quirks)
+{
+	int i;
+
+	/* init .data before setting .destroy */
+	dev->data = NULL;
+
+	dev->destroy = usb_msc_destroy;
+	dev->poll = usb_msc_poll;
+
 	dev->data = malloc (sizeof (usbmsc_inst_t));
 	if (!dev->data)
 		fatal("Not enough memory for USB MSC device.\n");
@@ -641,6 +650,7 @@
 	MSC_INST (dev)->bulk_in = 0;
 	MSC_INST (dev)->bulk_out = 0;
 	MSC_INST (dev)->usbdisk_created = 0;
+	MSC_INST (dev)->quirks = quirks;
 
 	for (i = 1; i <= dev->num_endp; i++) {
 		if (dev->endpoints[i].endpoint == 0)
diff --git a/payloads/libpayload/include/usb/usbmsc.h b/payloads/libpayload/include/usb/usbmsc.h
index f4562a5..8786586 100644
--- a/payloads/libpayload/include/usb/usbmsc.h
+++ b/payloads/libpayload/include/usb/usbmsc.h
@@ -34,13 +34,24 @@
 	unsigned int numblocks;
 	endpoint_t *bulk_in;
 	endpoint_t *bulk_out;
-	u8 usbdisk_created;
+	u8 quirks		: 7;
+	u8 usbdisk_created	: 1;
 	s8 ready;
 	u8 lun;
 	u8 num_luns;
 	void *data; /* For use by consumers of libpayload. */
 } usbmsc_inst_t;
 
+/* Possible values for quirks field. */
+enum {
+	/* Don't check for LUNs (force assumption that there's only one LUN). */
+	USB_MSC_QUIRK_NO_LUNS	= 1 << 0,
+	/* Never do a BULK_ONLY reset, just continue. This means that the device
+	   cannot recover from phase errors and won't detach automatically for
+	   unrecoverable errors. Do not use unless you have to. */
+	USB_MSC_QUIRK_NO_RESET	= 1 << 1,
+};
+
 /* Possible values for ready field. */
 enum {
 	USB_MSC_DETACHED = -1, /* Disk detached or out to lunch. */
@@ -56,4 +67,8 @@
 int readwrite_blocks_512 (usbdev_t *dev, int start, int n, cbw_direction dir, u8 *buf);
 int readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir, u8 *buf);
 
+/* Force a device to enumerate as MSC, without checking class/protocol types.
+   It must still have a bulk endpoint pair and respond to MSC commands. */
+void usb_msc_force_init (usbdev_t *dev, u32 quirks);
+
 #endif