make -R default, it doesn't hurt but may improve performance
[hmcfgusb] / hmcfgusb.c
index 9b00c84eecb43dbd51b88ce33f59dc81140bd213..f35138f6d7bbe22cddb13bf1b55074de818fc161 100644 (file)
 #include <math.h>
 #include <poll.h>
 #include <errno.h>
+#include <sys/time.h>
 #include <libusb-1.0/libusb.h>
 
 #include "hexdump.h"
 #include "hmcfgusb.h"
 
-#define USB_TIMEOUT            10000
+#define USB_TIMEOUT    10000
 
 #define ID_VENDOR      0x1b1f
 #define ID_PRODUCT     0xc00f
 #define EP_OUT         0x02
 #define EP_IN          0x83
 
+#define INTERFACE      0
+
 static int quit = 0;
+static int debug = 0;
 
 /* Not in all libusb-1.0 versions, so we have to roll our own :-( */
 static char * usb_strerror(int e)
@@ -116,13 +120,13 @@ static libusb_device_handle *hmcfgusb_find() {
                                return NULL;
                        }
 
-                       err = libusb_detach_kernel_driver(devh, 0);
+                       err = libusb_detach_kernel_driver(devh, INTERFACE);
                        if ((err != 0) && (err != LIBUSB_ERROR_NOT_FOUND)) {
                                fprintf(stderr, "Can't detach kernel driver: %s\n", usb_strerror(err));
                                return NULL;
                        }
 
-                       err = libusb_claim_interface(devh, 0);
+                       err = libusb_claim_interface(devh, INTERFACE);
                        if ((err != 0)) {
                                fprintf(stderr, "Can't claim interface: %s\n", usb_strerror(err));
                                return NULL;
@@ -136,31 +140,55 @@ static libusb_device_handle *hmcfgusb_find() {
        return NULL;
 }
 
+int hmcfgusb_send_null_frame(struct hmcfgusb_dev *usbdev)
+{
+       int err;
+       int cnt;
+
+       err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, NULL, 0, &cnt, USB_TIMEOUT);
+       if (err) {
+               fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
+               return 0;
+       }
+
+       return 1;
+}
+
 int hmcfgusb_send(struct hmcfgusb_dev *usbdev, unsigned char* send_data, int len, int done)
 {
        int err;
        int cnt;
-       int ret;
+       struct timeval tv_start, tv_end;
+       int msec;
+
+       if (debug) {
+               hexdump(send_data, len, "USB < ");
+       }
+
+       gettimeofday(&tv_start, NULL);
 
        err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, len, &cnt, USB_TIMEOUT);
        if (err) {
                fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
-               if (err == LIBUSB_ERROR_NO_DEVICE)
-                       exit(EXIT_FAILURE);
                return 0;
        }
 
        if (done) {
-               err = libusb_interrupt_transfer(usbdev->usb_devh, EP_OUT, send_data, 0, &cnt, USB_TIMEOUT);
-               if (err) {
-                       fprintf(stderr, "Can't send data: %s\n", usb_strerror(err));
-                       if (err == LIBUSB_ERROR_NO_DEVICE)
-                               exit(EXIT_FAILURE);
+               if (!hmcfgusb_send_null_frame(usbdev)) {
                        return 0;
                }
        }
 
-       return ret;
+       gettimeofday(&tv_end, NULL);
+       msec = ((tv_end.tv_sec-tv_start.tv_sec)*1000)+((tv_end.tv_usec-tv_start.tv_usec)/1000);
+
+       if (msec > 100) {
+               fprintf(stderr, "usb-transfer took more than 100ms (%dms), this may lead to timing problems!\n", msec);
+       } else if (debug) {
+               fprintf(stderr, "usb-transfer took %dms!\n", msec);
+       }
+
+       return 1;
 }
 
 static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh, libusb_transfer_cb_fn cb, void *data)
@@ -185,13 +213,12 @@ static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh,
        libusb_fill_interrupt_transfer(transfer, devh, EP_IN,
                        data_buf, ASYNC_SIZE, cb, data, USB_TIMEOUT);
 
-       transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK;
+       transfer->flags = LIBUSB_TRANSFER_SHORT_NOT_OK | LIBUSB_TRANSFER_FREE_BUFFER;
 
        err = libusb_submit_transfer(transfer);
        if (err != 0) {
                fprintf(stderr, "Can't submit transfer: %s\n", usb_strerror(err));
                libusb_free_transfer(transfer);
-               free(data_buf);
                return NULL;
        }
 
@@ -199,6 +226,7 @@ static struct libusb_transfer *hmcfgusb_prepare_int(libusb_device_handle *devh,
 }
 
 struct hmcfgusb_cb_data {
+       struct hmcfgusb_dev *dev;
        hmcfgusb_cb_fn cb;
        void *data;
 };
@@ -208,26 +236,47 @@ static void LIBUSB_CALL hmcfgusb_interrupt(struct libusb_transfer *transfer)
        int err;
        struct hmcfgusb_cb_data *cb_data;
 
+       cb_data = transfer->user_data;
+
        if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
                if (transfer->status != LIBUSB_TRANSFER_TIMED_OUT) {
                        fprintf(stderr, "Interrupt transfer not completed: %d!\n", transfer->status);
                        quit = EIO;
+
+                       if (cb_data && cb_data->dev && cb_data->dev->transfer) {
+                               libusb_free_transfer(cb_data->dev->transfer);
+                               cb_data->dev->transfer = NULL;
+                               free(cb_data);
+                       }
                        return;
                }
        } else {
-               cb_data = transfer->user_data;
                if (cb_data && cb_data->cb) {
-                       cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data);
+                       if (debug)
+                               hexdump(transfer->buffer, transfer->actual_length, "USB > ");
+
+                       if (!cb_data->cb(transfer->buffer, transfer->actual_length, cb_data->data)) {
+                               quit = EIO;
+
+                               if (cb_data && cb_data->dev && cb_data->dev->transfer) {
+                                       libusb_free_transfer(cb_data->dev->transfer);
+                                       cb_data->dev->transfer = NULL;
+                                       free(cb_data);
+                               }
+
+                               return;
+                       }
                } else {
-                       hexdump(transfer->buffer, transfer->actual_length, "RECV> ");
+                       hexdump(transfer->buffer, transfer->actual_length, "> ");
                }
        }
 
        err = libusb_submit_transfer(transfer);
        if (err != 0) {
                fprintf(stderr, "Can't re-submit transfer: %s\n", usb_strerror(err));
-               free(transfer->buffer);
                libusb_free_transfer(transfer);
+               cb_data->dev->transfer = NULL;
+               free(cb_data);
        }
 }
 
@@ -264,17 +313,21 @@ struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
        cb_data = malloc(sizeof(struct hmcfgusb_cb_data));
        if (!cb_data) {
                perror("Can't allocate memory for hmcfgusb_cb_data");
+               free(dev);
                return NULL;
        }
 
        memset(cb_data, 0, sizeof(struct hmcfgusb_cb_data));
 
+       cb_data->dev = dev;
        cb_data->cb = cb;
        cb_data->data = data;
 
        dev->transfer = hmcfgusb_prepare_int(devh, hmcfgusb_interrupt, cb_data);
        if (!dev->transfer) {
                fprintf(stderr, "Can't prepare async device io!\n");
+               free(dev);
+               free(cb_data);
                return NULL;
        }
 
@@ -282,6 +335,7 @@ struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
        if (!usb_pfd) {
                fprintf(stderr, "Can't get FDset from libusb!\n");
                free(dev);
+               free(cb_data);
                return NULL;
        }
 
@@ -292,6 +346,8 @@ struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
        dev->pfd = malloc(dev->n_usb_pfd * sizeof(struct pollfd));
        if (!dev->pfd) {
                perror("Can't allocate memory for poll-fds");
+               free(dev);
+               free(cb_data);
                return NULL;
        }
 
@@ -307,6 +363,8 @@ struct hmcfgusb_dev *hmcfgusb_init(hmcfgusb_cb_fn cb, void *data)
 
        dev->n_pfd = dev->n_usb_pfd;
 
+       quit = 0;
+
        return dev;
 }
 
@@ -360,6 +418,7 @@ int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
                n = poll(dev->pfd, dev->n_pfd, tv.tv_sec * 1000);
                if (n < 0) {
                        perror("poll");
+                       errno = 0;
                        return -1;
                } else if (n == 0) {
                        usb_event = 1;
@@ -370,6 +429,7 @@ int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
                                                usb_event = 1;
                                                break;
                                        } else {
+                                               errno = 0;
                                                return dev->pfd[fd_n].fd;
                                        }
                                }
@@ -387,8 +447,36 @@ int hmcfgusb_poll(struct hmcfgusb_dev *dev, int timeout)
                }
        }
 
-       if (quit)
+       errno = 0;
+       if (quit) {
+               fprintf(stderr, "closing device-connection due to error %d\n", quit);
                errno = quit;
+       }
 
        return -1;
 }
+
+void hmcfgusb_close(struct hmcfgusb_dev *dev)
+{
+       int err;
+
+       if (dev->transfer) {
+               libusb_cancel_transfer(dev->transfer);
+       }
+
+       err = libusb_release_interface(dev->usb_devh, INTERFACE);
+       if ((err != 0)) {
+               fprintf(stderr, "Can't release interface: %s\n", usb_strerror(err));
+       }
+
+       libusb_close(dev->usb_devh);
+       free(dev->pfd);
+       free(dev);
+
+       libusb_exit(NULL);
+}
+
+void hmcfgusb_set_debug(int d)
+{
+       debug = d;
+}
Impressum, Datenschutz