Browse Source

lowl-usb: Add tools for more reliable read/write using bulk transfers

Luke Dashjr 12 years ago
parent
commit
63f3e8fb4d
2 changed files with 125 additions and 0 deletions
  1. 117 0
      lowl-usb.c
  2. 8 0
      lowl-usb.h

+ 117 - 0
lowl-usb.c

@@ -180,6 +180,123 @@ void cgpu_copy_libusb_strings(struct cgpu_info *cgpu, libusb_device *usb)
 }
 }
 #endif
 #endif
 
 
+struct lowl_usb_endpoint {
+	struct libusb_device_handle *devh;
+	unsigned char endpoint_r;
+	int packetsz_r;
+	bytes_t _buf_r;
+	unsigned char endpoint_w;
+	int packetsz_w;
+};
+
+struct lowl_usb_endpoint *usb_open_ep(struct libusb_device_handle * const devh, const uint8_t epid, const int pktsz)
+{
+	struct lowl_usb_endpoint * const ep = malloc(sizeof(*ep));
+	ep->devh = devh;
+	if (epid & 0x80)
+	{
+		// Read endpoint
+		ep->endpoint_r = epid;
+		ep->packetsz_r = pktsz;
+		bytes_init(&ep->_buf_r);
+	}
+	else
+	{
+		// Write endpoint
+		ep->endpoint_w = epid;
+		ep->packetsz_w = epid;
+		ep->packetsz_r = -1;
+	}
+	return ep;
+};
+
+struct lowl_usb_endpoint *usb_open_ep_pair(struct libusb_device_handle * const devh, const uint8_t epid_r, const int pktsz_r, const uint8_t epid_w, const int pktsz_w)
+{
+	struct lowl_usb_endpoint * const ep = malloc(sizeof(*ep));
+	*ep = (struct lowl_usb_endpoint){
+		.devh = devh,
+		.endpoint_r = epid_r,
+		.packetsz_r = pktsz_r,
+		._buf_r = BYTES_INIT,
+		.endpoint_w = epid_w,
+		.packetsz_w = pktsz_w,
+	};
+	return ep;
+}
+
+ssize_t usb_read(struct lowl_usb_endpoint * const ep, void * const data, size_t datasz)
+{
+	size_t xfer;
+	if ( (xfer = bytes_len(&ep->_buf_r)) < datasz)
+	{
+		bytes_extend_buf(&ep->_buf_r, datasz + ep->packetsz_r - 1);
+		unsigned char *p = &bytes_buf(&ep->_buf_r)[xfer];
+		int pxfer;
+		int rem = datasz - xfer, rsz;
+		while (rem > 0)
+		{
+			rsz = (rem / ep->packetsz_r) * ep->packetsz_r;
+			if (rsz < rem)
+				rsz += ep->packetsz_r;
+			switch (libusb_bulk_transfer(ep->devh, ep->endpoint_r, p, rsz, &pxfer, 0))
+			{
+				case 0:
+				case LIBUSB_ERROR_TIMEOUT:
+					p += pxfer;
+					rem -= pxfer;
+					// NOTE: Need to maintain _buf_r length so data is saved in case of error
+					xfer += pxfer;
+					bytes_resize(&ep->_buf_r, xfer);
+					break;
+				case LIBUSB_ERROR_PIPE:
+				case LIBUSB_ERROR_NO_DEVICE:
+					errno = EPIPE;
+					return -1;
+				default:
+					errno = EIO;
+					return -1;
+			}
+		}
+	}
+	memcpy(data, bytes_buf(&ep->_buf_r), datasz);
+	bytes_shift(&ep->_buf_r, datasz);
+	return datasz;
+}
+
+ssize_t usb_write(struct lowl_usb_endpoint * const ep, const void * const data, size_t datasz)
+{
+	unsigned char *p = (void*)data;
+	size_t rem = datasz;
+	int pxfer;
+	while (rem > 0)
+	{
+		switch (libusb_bulk_transfer(ep->devh, ep->endpoint_w, p, rem, &pxfer, 0))
+		{
+			case 0:
+			case LIBUSB_ERROR_TIMEOUT:
+				p += pxfer;
+				rem -= pxfer;
+				break;
+			case LIBUSB_ERROR_PIPE:
+			case LIBUSB_ERROR_NO_DEVICE:
+				errno = EPIPE;
+				return (datasz - rem) ?: -1;
+			default:
+				errno = EIO;
+				return (datasz - rem) ?: -1;
+		}
+	}
+	errno = 0;
+	return datasz;
+}
+
+void usb_close_ep(struct lowl_usb_endpoint * const ep)
+{
+	if (ep->packetsz_r != -1)
+		bytes_free(&ep->_buf_r);
+	free(ep);
+}
+
 void lowl_usb_close(struct libusb_device_handle * const devh)
 void lowl_usb_close(struct libusb_device_handle * const devh)
 {
 {
 	libusb_close(devh);
 	libusb_close(devh);

+ 8 - 0
lowl-usb.h

@@ -9,4 +9,12 @@ extern struct device_drv *bfg_claim_usb(struct device_drv * const, const bool ve
 
 
 extern void cgpu_copy_libusb_strings(struct cgpu_info *, libusb_device *);
 extern void cgpu_copy_libusb_strings(struct cgpu_info *, libusb_device *);
 
 
+struct lowl_usb_endpoint;
+
+extern struct lowl_usb_endpoint *usb_open_ep(struct libusb_device_handle *, uint8_t epid, int pktsz);
+extern struct lowl_usb_endpoint *usb_open_ep_pair(struct libusb_device_handle *, uint8_t epid_r, int pktsz_r, uint8_t epid_w, int pktsz_w);
+extern ssize_t usb_read(struct lowl_usb_endpoint *, void *, size_t);
+extern ssize_t usb_write(struct lowl_usb_endpoint *, const void *, size_t);
+extern void usb_close_ep(struct lowl_usb_endpoint *);
+
 #endif
 #endif