Windows: Support LIBUSB_TRANSFER_ADD_ZERO_PACKET on winusb

Closes #312

Signed-off-by: Nathan Hjelm <hjelmn@google.com>
diff --git a/libusb/os/windows_common.h b/libusb/os/windows_common.h
index c84f1f9..2de2b60 100644
--- a/libusb/os/windows_common.h
+++ b/libusb/os/windows_common.h
@@ -265,12 +265,19 @@
 	// Not currently used
 	char dummy;
 };
+ 
+enum WINUSB_ZLP {
+	WINUSB_ZLP_UNSET = 0,
+	WINUSB_ZLP_OFF = 1,
+	WINUSB_ZLP_ON = 2
+};
 
 struct winusb_device_handle_priv {
 	int active_interface;
 	struct {
 		HANDLE dev_handle; // WinUSB needs an extra handle for the file
 		HANDLE api_handle; // used by the API to communicate with the device
+		uint8_t zlp[USB_MAXENDPOINTS]; // Current per-endpoint SHORT_PACKET_TERMINATE status (enum WINUSB_ZLP)
 	} interface_handle[USB_MAXINTERFACES];
 	int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
 };
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c
index e4d194a..877a2ab 100644
--- a/libusb/os/windows_winusb.c
+++ b/libusb/os/windows_winusb.c
@@ -2024,8 +2024,6 @@
 		break;
 	case LIBUSB_TRANSFER_TYPE_BULK:
 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
-		if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
-			return LIBUSB_ERROR_NOT_SUPPORTED;
 		transfer_fn = priv->apib->submit_bulk_transfer;
 		break;
 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
@@ -2474,6 +2472,7 @@
 			continue; // Other policies don't apply to control endpoint or libusb0
 
 		policy = false;
+		handle_priv->interface_handle[iface].zlp[endpoint_address] = WINUSB_ZLP_UNSET;
 		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
 			SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy))
 			usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address);
@@ -3041,6 +3040,23 @@
 		usbi_dbg("reading %d bytes", transfer->length);
 		ret = WinUSBX[sub_api].ReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
 	} else {
+		// Set SHORT_PACKET_TERMINATE if ZLP requested.
+		// Changing this can be a problem with packets in flight, so only allow on the first transfer.
+		UCHAR policy = (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET) != 0;
+		uint8_t* current_zlp = &handle_priv->interface_handle[current_interface].zlp[transfer->endpoint];
+		if (*current_zlp == WINUSB_ZLP_UNSET) {
+			if (policy &&
+				!WinUSBX[sub_api].SetPipePolicy(winusb_handle, transfer->endpoint,
+				SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) {
+				usbi_err(TRANSFER_CTX(transfer), "failed to set SHORT_PACKET_TERMINATE for endpoint %02X", transfer->endpoint);
+				return LIBUSB_ERROR_NOT_SUPPORTED;
+			}
+			*current_zlp = policy ? WINUSB_ZLP_ON : WINUSB_ZLP_OFF;
+		} else if (policy != (*current_zlp == WINUSB_ZLP_ON)) {
+			usbi_err(TRANSFER_CTX(transfer), "cannot change ZERO_PACKET for endpoint %02X on Windows", transfer->endpoint);
+			return LIBUSB_ERROR_NOT_SUPPORTED;
+		}
+
 		usbi_dbg("writing %d bytes", transfer->length);
 		ret = WinUSBX[sub_api].WritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
 	}
@@ -4006,6 +4022,9 @@
 	UNUSED(sub_api);
 	CHECK_HID_AVAILABLE;
 
+	if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+
 	transfer_priv->hid_dest = NULL;
 	safe_free(transfer_priv->hid_buffer);
 
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index f8f80a3..53d05fe 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11620
+#define LIBUSB_NANO 11621