Commit c30af6d6634efaabde6fbd40519bb279aad134ac

kanoi 2013-06-04T08:21:03

Merge pull request #436 from kanoi/master usbutils optional read buffering

diff --git a/usbutils.c b/usbutils.c
index 9a7cca6..99d1d4c 100644
--- a/usbutils.c
+++ b/usbutils.c
@@ -1311,6 +1311,9 @@ static struct cg_usb_device *free_cgusb(struct cg_usb_device *cgusb)
 
 	free(cgusb->found);
 
+	if (cgusb->buffer)
+		free(cgusb->buffer);
+
 	free(cgusb);
 
 	return NULL;
@@ -2024,6 +2027,36 @@ static void rejected_inc(struct cgpu_info *cgpu, uint32_t mode)
 }
 #endif
 
+static char *find_end(unsigned char *buf, unsigned char *ptr, int ptrlen, int tot, char *end, int endlen, bool first)
+{
+	unsigned char *search;
+
+	if (endlen > tot)
+		return NULL;
+
+	// If end is only 1 char - do a faster search
+	if (endlen == 1) {
+		if (first)
+			search = buf;
+		else
+			search = ptr;
+
+		return strchr((char *)search, *end);
+	} else {
+		if (first)
+			search = buf;
+		else {
+			// must allow end to have been chopped in 2
+			if ((tot - ptrlen) >= (endlen - 1))
+				search = ptr - (endlen - 1);
+			else
+				search = ptr - (tot - ptrlen);
+		}
+
+		return strstr((char *)search, end);
+	}
+}
+
 #define USB_MAX_READ 8192
 
 int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, const char *end, __maybe_unused enum usb_cmds cmd, bool readonce)
@@ -2037,8 +2070,8 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 	unsigned int initial_timeout;
 	double max, done;
 	int bufleft, err, got, tot;
-	__maybe_unused bool first = true;
-	unsigned char *search;
+	bool first = true;
+	char *search;
 	int endlen;
 
 	// We add 4: 1 for null, 2 for FTDI status and 1 to round to 4 bytes
@@ -2064,19 +2097,34 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 		timeout = usbdev->found->timeout;
 
 	if (end == NULL) {
-		tot = 0;
-		ptr = usbbuf;
-		bufleft = bufsiz;
+		if (usbdev->buffer && usbdev->bufamt) {
+			tot = usbdev->bufamt;
+			bufleft = bufsiz - tot;
+			memcpy(usbbuf, usbdev->buffer, tot);
+			ptr = usbbuf + tot;
+			usbdev->bufamt = 0;
+		} else {
+			tot = 0;
+			bufleft = bufsiz;
+			ptr = usbbuf;
+		}
+
 		err = LIBUSB_SUCCESS;
 		initial_timeout = timeout;
 		max = ((double)timeout) / 1000.0;
 		cgtime(&read_start);
 		while (bufleft > 0) {
-			if (ftdi)
-				usbbufread = bufleft + 2;
-			else
-				usbbufread = bufleft;
+			// TODO: use (USB_MAX_READ - tot) always?
+			if (usbdev->buffer)
+				usbbufread = USB_MAX_READ - tot;
+			else {
+				if (ftdi)
+					usbbufread = bufleft + 2;
+				else
+					usbbufread = bufleft;
+			}
 			got = 0;
+
 			STATS_TIMEVAL(&tv_start);
 			err = libusb_bulk_transfer(usbdev->handle,
 					usbdev->found->eps[ep].ep,
@@ -2120,6 +2168,16 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 				break;
 		}
 
+		// N.B. usbdev->buffer was emptied before the while() loop
+		if (usbdev->buffer && tot > (int)bufsiz) {
+			usbdev->bufamt = tot - bufsiz;
+			memcpy(usbdev->buffer, ptr + bufsiz, usbdev->bufamt);
+			tot -= usbdev->bufamt;
+			usbbuf[tot] = '\0';
+			applog(LOG_ERR, "USB: %s%i read1 buffering %d extra bytes",
+					cgpu->drv->name, cgpu->device_id, usbdev->bufamt);
+		}
+
 		*processed = tot;
 		memcpy((char *)buf, (const char *)usbbuf, (tot < (int)bufsiz) ? tot + 1 : (int)bufsiz);
 
@@ -2129,19 +2187,33 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 		return err;
 	}
 
-	tot = 0;
-	ptr = usbbuf;
-	bufleft = bufsiz;
+	if (usbdev->buffer && usbdev->bufamt) {
+		tot = usbdev->bufamt;
+		bufleft = bufsiz - tot;
+		memcpy(usbbuf, usbdev->buffer, tot);
+		ptr = usbbuf + tot;
+		usbdev->bufamt = 0;
+	} else {
+		tot = 0;
+		bufleft = bufsiz;
+		ptr = usbbuf;
+	}
+
 	endlen = strlen(end);
 	err = LIBUSB_SUCCESS;
 	initial_timeout = timeout;
 	max = ((double)timeout) / 1000.0;
 	cgtime(&read_start);
 	while (bufleft > 0) {
-		if (ftdi)
-			usbbufread = bufleft + 2;
-		else
-			usbbufread = bufleft;
+		// TODO: use (USB_MAX_READ - tot) always?
+		if (usbdev->buffer)
+			usbbufread = USB_MAX_READ - tot;
+		else {
+			if (ftdi)
+				usbbufread = bufleft + 2;
+			else
+				usbbufread = bufleft;
+		}
 		got = 0;
 		STATS_TIMEVAL(&tv_start);
 		err = libusb_bulk_transfer(usbdev->handle,
@@ -2172,23 +2244,8 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 		if (err || readonce)
 			break;
 
-		// WARNING - this will return data past END ('if' there is extra data)
-		if (endlen <= tot) {
-			// If END is only 1 char - do a faster search
-			if (endlen == 1) {
-				if (strchr((char *)ptr, *end))
-					break;
-			} else {
-				// must allow END to have been chopped in 2 transfers
-				if ((tot - got) >= (endlen - 1))
-					search = ptr - (endlen - 1);
-				else
-					search = ptr - (tot - got);
-
-				if (strstr((char *)search, end))
-					break;
-			}
-		}
+		if (find_end(usbbuf, ptr, got, tot, (char *)end, endlen, first))
+			break;
 
 		ptr += got;
 		bufleft -= got;
@@ -2204,6 +2261,38 @@ int _usb_read(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *pro
 			break;
 	}
 
+	if (usbdev->buffer) {
+		bool dobuffer = false;
+
+		if ((search = find_end(usbbuf, usbbuf, tot, tot, (char *)end, endlen, true))) {
+			// end finishes after bufsiz
+			if ((search + endlen - (char *)usbbuf) > (int)bufsiz) {
+				usbdev->bufamt = tot - bufsiz;
+				dobuffer = true;
+			} else {
+				// extra data after end
+				if (*(search + endlen)) {
+					usbdev->bufamt = tot - (search + endlen - (char *)usbbuf);
+					dobuffer = true;
+				}
+			}
+		} else {
+			// no end, but still bigger than bufsiz
+			if (tot > (int)bufsiz) {
+				usbdev->bufamt = tot - bufsiz;
+				dobuffer = true;
+			}
+		}
+
+		if (dobuffer) {
+			tot -= usbdev->bufamt;
+			memcpy(usbdev->buffer, usbbuf + tot, usbdev->bufamt);
+			usbbuf[tot] = '\0';
+			applog(LOG_ERR, "USB: %s%i read2 buffering %d extra bytes",
+					cgpu->drv->name, cgpu->device_id, usbdev->bufamt);
+		}
+	}
+
 	*processed = tot;
 	memcpy((char *)buf, (const char *)usbbuf, (tot < (int)bufsiz) ? tot + 1 : (int)bufsiz);
 
@@ -2400,6 +2489,45 @@ int usb_ftdi_cts(struct cgpu_info *cgpu)
 	return (ret & FTDI_RS0_CTS);
 }
 
+void usb_buffer_enable(struct cgpu_info *cgpu)
+{
+	struct cg_usb_device *cgusb = cgpu->usbdev;
+
+	if (!cgusb->buffer) {
+		cgusb->bufamt = 0;
+		cgusb->buffer = malloc(USB_MAX_READ+1);
+		if (!cgusb->buffer)
+			quit(1, "Failed to malloc buffer for USB %s%i",
+				cgpu->drv->name, cgpu->device_id);
+		cgusb->bufsiz = USB_MAX_READ;
+	}
+}
+
+void usb_buffer_disable(struct cgpu_info *cgpu)
+{
+	struct cg_usb_device *cgusb = cgpu->usbdev;
+
+	if (cgusb->buffer) {
+		cgusb->bufamt = 0;
+		cgusb->bufsiz = 0;
+		free(cgusb->buffer);
+	}
+}
+
+void usb_buffer_clear(struct cgpu_info *cgpu)
+{
+	struct cg_usb_device *cgusb = cgpu->usbdev;
+
+	cgusb->bufamt = 0;
+}
+
+uint32_t usb_buffer_size(struct cgpu_info *cgpu)
+{
+	struct cg_usb_device *cgusb = cgpu->usbdev;
+
+	return cgusb->bufamt;
+}
+
 void usb_cleanup()
 {
 	struct cgpu_info *cgpu;
diff --git a/usbutils.h b/usbutils.h
index 5ff90c2..71aa8fb 100644
--- a/usbutils.h
+++ b/usbutils.h
@@ -167,6 +167,9 @@ struct cg_usb_device {
 	char *serial_string;
 	unsigned char fwVersion;	// ??
 	unsigned char interfaceVersion;	// ??
+	char *buffer;
+	uint32_t bufsiz;
+	uint32_t bufamt;
 };
 
 struct cg_usb_info {
@@ -258,6 +261,10 @@ int usb_ftdi_ctw(struct cgpu_info *cgpu);
 int _usb_write(struct cgpu_info *cgpu, int ep, char *buf, size_t bufsiz, int *processed, unsigned int timeout, enum usb_cmds);
 int _usb_transfer(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint32_t *data, int siz, unsigned int timeout, enum usb_cmds cmd);
 int _usb_transfer_read(struct cgpu_info *cgpu, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, char *buf, int bufsiz, int *amount, unsigned int timeout, enum usb_cmds cmd);
+void usb_buffer_enable(struct cgpu_info *cgpu);
+void usb_buffer_disable(struct cgpu_info *cgpu);
+void usb_buffer_clear(struct cgpu_info *cgpu);
+uint32_t usb_buffer_size(struct cgpu_info *cgpu);
 void usb_cleanup();
 void usb_initialise();
 void *usb_resource_thread(void *userdata);