Index: src/linux.c =================================================================== --- src/linux.c (revision 3) +++ src/linux.c (working copy) @@ -19,8 +19,13 @@ #include "usbi.h" +#define ISOC_URB_MAX_NUM 10 +#define RESUBMIT 1 +#define NO_RESUBMIT 0 + static pthread_t event_thread; static int event_pipe[2]; +static int resubmit_flag = RESUBMIT; static pthread_mutex_t usbi_ios_lock = PTHREAD_MUTEX_INITIALIZER; static struct list_head usbi_ios = { .prev = &usbi_ios, .next = &usbi_ios }; @@ -63,9 +68,22 @@ static int linux_close(struct usbi_dev_handle *hdev) { + struct usbi_io *io, *tio; + if (hdev->fd < 0) return 0; + resubmit_flag = NO_RESUBMIT; /*stop isoc resubmit, it is a method, but not a good method. And it will be improved in near future*/ + + pthread_mutex_lock(&usbi_ios_lock); + tio = NULL; + list_for_each_entry(io, &hdev->ios, list) { + usbi_free_io(tio); + tio = io; + } + usbi_free_io(tio); + list_del(&hdev->io_list); + pthread_mutex_unlock(&usbi_ios_lock); if (close(hdev->fd) == -1) /* Failing trying to close a file really isn't an error, so return 0 */ usbi_debug(2, "error closing device fd %d: %s", hdev->fd, strerror(errno)); @@ -121,6 +139,13 @@ { int ret; + /*stop isoc resubmit,currently it is only a work around , + but not a good solution. And it will be improved in near future, + maybe we need a stop_isoc() function to do it*/ + resubmit_flag = NO_RESUBMIT; + + /*wait for isoc urb to be finished, so isoc callback is better to return quickly*/ + sleep(3); ret = ioctl(hdev->fd, IOCTL_USB_RELEASEINTF, &interface); if (ret < 0) { usbi_debug(1, "could not release interface %d: %s", interface, strerror(errno)); @@ -183,7 +208,6 @@ io->urb.endpoint = io->endpoint; io->urb.status = 0; - io->urb.flags = 0; io->urb.signr = 0; io->urb.usercontext = (void *)io; @@ -194,20 +218,21 @@ if (list_empty(&hdev->ios)) { list_add(&hdev->io_list, &usbi_ios); memcpy(&hdev->tvo, &io->tvo, sizeof(hdev->tvo)); - } else if (usbi_timeval_compare(&io->tvo, &hdev->tvo) < 0) + } else if (usbi_timeval_compare(&io->tvo, &hdev->tvo) < 0 && io->type == USBI_IO_ISOCHRONOUS) memcpy(&hdev->tvo, &io->tvo, sizeof(hdev->tvo)); list_add(&io->list, &hdev->ios); pthread_mutex_unlock(&usbi_ios_lock); - ret = ioctl(hdev->fd, IOCTL_USB_SUBMITURB, &io->urb); if (ret < 0) { usbi_debug(1, "error submitting URB: %s", strerror(errno)); - + pthread_mutex_lock(&usbi_ios_lock); io->inprogress = 0; - list_del(&hdev->io_list); list_del(&io->list); + if(list_empty(&hdev->ios)) { + list_del(&hdev->io_list); + } pthread_mutex_unlock(&usbi_ios_lock); /* FIXME: Convert error codes? */ @@ -268,36 +293,186 @@ return urb_submit(hdev, io); } +static struct usbi_io * isoc_io_clone(struct usbi_io *io) +{ + struct usbi_io *new_io = NULL; + struct usbk_iso_packet_desc *p; + struct libusb_isoc_packet *packets; + struct libusb_isoc_request *isocr; + void *new_buf; + char *tmp; + int buflen = 0; + int i = 0; + int new_iolen; + + /*create new_io for descriptor space of num_packets*/ + new_io = (struct usbi_io *)malloc(sizeof(*io) + io->isoc.request->num_packets * sizeof(struct usbk_iso_packet_desc)); + + if(!new_io) { + usbi_debug(1, "error malloc new_io\n", strerror(errno)); + return NULL; + } + new_iolen = sizeof(*io) + io->isoc.request->num_packets + * sizeof(struct usbk_iso_packet_desc); + memset(new_io, 0, new_iolen); + memcpy(new_io, io, sizeof(*io)); + + /*initialize new io structure*/ + pthread_mutex_init(&new_io->lock, NULL); + pthread_cond_init(&new_io->cond, NULL); + list_init(&new_io->list); + + new_io->urb.type = USBK_URB_TYPE_ISO; + + isocr = new_io->isoc.request; + packets = isocr->packets; + + /*isoc.num_packets seems no need, because isoc request has included this member*/ + new_io->isoc.num_packets = isocr->num_packets; + for(i = 0; i < isocr->num_packets; i++) + { + buflen += packets[i].buflen; + } + + new_buf = malloc(buflen); + if(!new_buf) + { + free(new_io); + return NULL; + } + memset(new_buf, 0, buflen); + + new_io->urb.buffer = new_buf; + new_io->urb.buffer_length = buflen; + new_io->urb.start_frame = isocr->start_frame; + new_io->urb.number_of_packets = isocr->num_packets; + new_io->urb.flags = isocr->flags; + + p = &new_io->urb.iso_frame_desc[0]; + tmp = new_io->urb.buffer; + for(i = 0; i < new_io->urb.number_of_packets; i++) { + p[i].length = packets[i].buflen; + p[i].actual_length = 0; + p[i].status = 0; + /*while usb isoc out transfer*/ + if( (isocr->endpoint & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_OUT) { + memcpy((void *)tmp, packets[i].buf, packets[i].buflen); + tmp += packets[i].buflen; + } + } + return new_io; +} + + static int linux_submit_isoc(struct usbi_dev_handle *hdev, struct usbi_io *io) { - /* FIXME: Implement */ - return LIBUSB_NOT_SUPPORTED; + int n = 0; + int ret; + struct usbi_io *new_io; + + if(io == NULL || hdev == NULL) + return LIBUSB_BADARG; + + resubmit_flag = RESUBMIT; + for(n = 0; n < ISOC_URB_MAX_NUM; n++) + { + usleep(1000); + + new_io = isoc_io_clone(io); + if(new_io != NULL) + { + ret = urb_submit(hdev, new_io); + if(ret < 0) + { + usbi_debug(1, "submit isoc urb error!\n", strerror(errno)); + return ret; + } + } + } + return 0; } /* FIXME: Make sure there aren't any race conditions here */ static int io_complete(struct usbi_dev_handle *hdev) { struct usbk_urb *urb; - struct usbi_io *io; + struct usbi_io *io, *tio; + struct usbi_io *new_io; + struct timeval tvo = {.tv_sec = 0, .tv_usec = 0}; + struct libusb_isoc_packet *packets; + int i, offset = 0; int ret; - ret = ioctl(hdev->fd, IOCTL_USB_REAPURBNDELAY, (void *)&urb); - if (ret < 0) { - usbi_debug(1, "error reaping URB: %s", strerror(errno)); - return LIBUSB_FAILURE; - } + while((ret = ioctl(hdev->fd, IOCTL_USB_REAPURBNDELAY, (void *)&urb)) >= 0) { + io = urb->usercontext; + list_del(&io->list); /* lock obtained by caller */ + if(list_empty(&hdev->ios)) + { + list_del(&hdev->io_list); + } - io = urb->usercontext; - list_del(&io->list); /* lock obtained by caller */ /* FIXME: Translate the status code */ - if (io->type == USBI_IO_CONTROL && io->ctrl.setup) + { + memcpy(io->ctrl.buf, io->urb.buffer + USBI_CONTROL_SETUP_LEN, io->ctrl.buflen); +} + /*complete handle for isochronous transfer*/ + if(io->type == USBI_IO_ISOCHRONOUS) + { + /*copy data*/ + packets = io->isoc.request->packets; + io->isoc.results = (struct libusb_isoc_result *)malloc(io->isoc.request->num_packets * sizeof(struct libusb_isoc_result)); + if(io->isoc.results == NULL) + { + if(io->urb.buffer) + free(io->urb.buffer); + usbi_free_io(io); + continue; + } + + /*copy the result of each packet*/ + offset = 0; + for(i = 0; i < io->isoc.request->num_packets; i++) + { + memcpy(packets[i].buf, io->urb.buffer + offset, packets[i].buflen); + offset += packets[i].buflen; + io->isoc.results[i].status = urb->iso_frame_desc[i].status; + io->isoc.results[i].transferred_bytes = urb->iso_frame_desc[i].actual_length; + } + if(io->urb.buffer) + free(io->urb.buffer); + /*resubmit isoc io*/ + if(resubmit_flag == RESUBMIT) + { + new_io = isoc_io_clone(io); + if(new_io != NULL) + { + pthread_mutex_unlock(&usbi_ios_lock); + urb_submit(hdev, new_io); + pthread_mutex_lock(&usbi_ios_lock); + } + } + } /* FIXME: Should this be done without a lock held so the completion handler can callback into this code? */ + + pthread_mutex_unlock(&usbi_ios_lock); usbi_io_complete(io, urb->status, urb->actual_length); - + pthread_mutex_lock(&usbi_ios_lock); + + } + + list_for_each_entry_safe(io, tio, &hdev->ios, list) { + if(io->type == USBI_IO_ISOCHRONOUS) + continue; + if(!tvo.tv_sec || usbi_timeval_compare(&io->tvo, &tvo) < 0) + memcpy(&tvo, &io->tvo, sizeof(tvo)); + } + if(!(tvo.tv_sec == 0 && tvo.tv_usec == 0)) + memcpy(&hdev->tvo, &tvo, sizeof(hdev->tvo)); + return 0; } @@ -307,10 +482,18 @@ struct usbi_io *io, *tio; list_for_each_entry_safe(io, tio, &hdev->ios, list) { + /*currently, isochronous io doesn't consider timeout issue*/ + if(io->type == USBI_IO_ISOCHRONOUS) + continue; + if (usbi_timeval_compare(&io->tvo, tvc) <= 0) { int ret; list_del(&io->list); + if(list_empty(&hdev->ios)) + { + list_del(&hdev->io_list); + } ret = ioctl(hdev->fd, IOCTL_USB_DISCARDURB, &io->urb); if (ret < 0) { @@ -336,7 +519,7 @@ ret = ioctl(io->dev->fd, IOCTL_USB_DISCARDURB, &io->urb); if (ret < 0) { - usbi_debug(1, "error cancelling URB: %s", strerror(errno)); + usbi_debug(1, "error cancelling URB: %s errno = %d", strerror(errno), errno); return LIBUSB_FAILURE; } @@ -383,7 +566,7 @@ maxfd = dev->fd; if (dev->tvo.tv_sec && - (!tvo.tv_sec || usbi_timeval_compare(&dev->tvo, &tvo))) + (!tvo.tv_sec || usbi_timeval_compare(&dev->tvo, &tvo) < 0)) /* New soonest timeout */ memcpy(&tvo, &dev->tvo, sizeof(tvo)); } @@ -429,12 +612,9 @@ list_for_each_entry_safe(dev, tdev, &usbi_ios, io_list) { if (FD_ISSET(dev->fd, &writefds)) io_complete(dev); - + if (usbi_timeval_compare(&dev->tvo, &tvc) <= 0) io_timeout(dev, &tvc); - - if (list_empty(&dev->ios)) - list_del(&dev->io_list); } pthread_mutex_unlock(&usbi_ios_lock); } Index: src/async.c =================================================================== --- src/async.c (revision 3) +++ src/async.c (working copy) @@ -61,6 +61,8 @@ void usbi_free_io(struct usbi_io *io) { + if( io == NULL) + return; pthread_mutex_lock(&io->lock); if (io->inprogress) @@ -175,7 +177,6 @@ io->ctrl.setup = setup; io->ctrl.buf = ctrl->buf; io->ctrl.buflen = ctrl->buflen; - ret = dev->idev->ops->submit_ctrl(dev, io); if (ret < 0) { usbi_free_io(io); Index: src/usb.c =================================================================== --- src/usb.c (revision 3) +++ src/usb.c (working copy) @@ -457,7 +457,6 @@ struct simple_io io; simple_io_setup(&io); - ret = usbi_async_ctrl_submit(ctrl, ctrl_callback, &io); if (ret) return ret; @@ -468,7 +467,6 @@ return ret; } else { - return LIBUSB_FAILURE; } }