/* * FIXME: Packetize large buffers here. 2.4 HCDs (atleast, haven't checked * 2.5 HCDs yet) don't handle multi-packet Interrupt transfers. So we need * to lookup the endpoint packet size and packetize appropriately here. */ int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { /* Ensure the endpoint address is correct */ return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size, timeout); }
int usb_bulk_write(usb_dev_handle *dev, int ep, const char *bytes, int size, int timeout) { /* Ensure the endpoint address is correct */ return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, (char *)bytes, size, timeout); }
int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { /* Ensure the endpoint address is correct */ ep |= USB_ENDPOINT_IN; return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, timeout); }
/* Reading and writing are the same except for the endpoint */ static int usb_urb_transfer_sp(usb_dev_handle *dev, int ep, int urbtype, char *bytes, int size, int timeout, int *actual_length, int max_rw) { struct usb_urb urb; int bytesdone = 0, requested; struct timeval tv, tv_ref, tv_now; struct usb_urb *context; int ret, waiting; /* * HACK: The use of urb.usercontext is a hack to get threaded applications * sort of working again. Threaded support is still not recommended, but * this should allow applications to work in the common cases. Basically, * if we get the completion for an URB we're not waiting for, then we update * the usercontext pointer to 1 for the other threads URB and it will see * the change after it wakes up from the the timeout. Ugly, but it works. */ /* * Get actual time, and add the timeout value. The result is the absolute * time where we have to quit waiting for an message. */ gettimeofday(&tv_ref, NULL); tv_ref.tv_sec = tv_ref.tv_sec + timeout / 1000; tv_ref.tv_usec = tv_ref.tv_usec + (timeout % 1000) * 1000; if (tv_ref.tv_usec > 1000000) { tv_ref.tv_usec -= 1000000; tv_ref.tv_sec++; } do { fd_set writefds; requested = size - bytesdone; if (requested > max_rw) requested = max_rw; urb.type = urbtype; urb.endpoint = ep; urb.flags = 0; urb.buffer = bytes + bytesdone; urb.buffer_length = requested; urb.signr = 0; urb.actual_length = 0; urb.number_of_packets = 0; /* don't do isochronous yet */ urb.usercontext = NULL; ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb); if (ret < 0) { USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno)); return ret; } FD_ZERO(&writefds); FD_SET(dev->fd, &writefds); restart: waiting = 1; context = NULL; while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) { tv.tv_sec = 0; tv.tv_usec = 1000; // 1 msec select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait #if 0 if (timeout) { #else /* 32767 is a magic number for no timeout */ if (timeout && timeout != 32767) { #endif /* compare with actual time, as the select timeout is not that precise */ gettimeofday(&tv_now, NULL); if ((tv_now.tv_sec > tv_ref.tv_sec) || ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec))) waiting = 0; } } if (context && context != &urb) { context->usercontext = URB_USERCONTEXT_COOKIE; /* We need to restart since we got a successful URB, but not ours */ goto restart; } /* * If there was an error, that wasn't EAGAIN (no completion), then * something happened during the reaping and we should return that * error now */ if (ret < 0 && !urb.usercontext && errno != EAGAIN) USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno)); bytesdone += urb.actual_length; *actual_length = bytesdone; } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested); /* If the URB didn't complete in success or error, then let's unlink it */ if (ret < 0 && !urb.usercontext) { int rc; if (!waiting) rc = -ETIMEDOUT; else rc = urb.status; ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb); if (ret < 0 && errno != EINVAL && usb_debug >= 1) fprintf(stderr, "error discarding URB: %s", strerror(errno)); /* * When the URB is unlinked, it gets moved to the completed list and * then we need to reap it or else the next time we call this function, * we'll get the previous completion and exit early */ ioctl(dev->fd, IOCTL_USB_REAPURB, &context); return rc; } return bytesdone; } int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { /* Ensure the endpoint address is correct */ return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, timeout); } int usb_bulk_write_sp(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout, int *actual_length, int max_rw) { /* Ensure the endpoint address is correct */ return usb_urb_transfer_sp(dev, ep, USB_URB_TYPE_BULK, bytes, size, timeout, actual_length, max_rw); } int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) { /* Ensure the endpoint address is correct */ ep |= USB_ENDPOINT_IN; return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, timeout); }