diff --git a/include/libdivecomputer/Makefile.am b/include/libdivecomputer/Makefile.am
index bffc849..6f679c7 100644
--- a/include/libdivecomputer/Makefile.am
+++ b/include/libdivecomputer/Makefile.am
@@ -12,6 +12,7 @@ libdivecomputer_HEADERS = \
bluetooth.h \
ble.h \
irda.h \
+ usb.h \
usbhid.h \
custom.h \
device.h \
diff --git a/include/libdivecomputer/usb.h b/include/libdivecomputer/usb.h
new file mode 100644
index 0000000..6d13813
--- /dev/null
+++ b/include/libdivecomputer/usb.h
@@ -0,0 +1,145 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2020 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef DC_USB_H
+#define DC_USB_H
+
+#include "common.h"
+#include "context.h"
+#include "iostream.h"
+#include "iterator.h"
+#include "descriptor.h"
+#include "ioctl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * Perform a USB control transfer.
+ *
+ * The parameters for the control transfer are specified in the
+ * #dc_usb_control_t data structure. If the control transfer requires
+ * additional data as in- or output, the buffer must be located
+ * immediately after the #dc_usb_control_t data structure, and the
+ * length of the buffer must be indicated in the #wLength field. The
+ * size of the ioctl request is the total size, including the size of
+ * the #dc_usb_control_t structure.
+ */
+#define DC_IOCTL_USB_CONTROL_READ DC_IOCTL_IOR('u', 0, DC_IOCTL_SIZE_VARIABLE)
+#define DC_IOCTL_USB_CONTROL_WRITE DC_IOCTL_IOW('u', 0, DC_IOCTL_SIZE_VARIABLE)
+
+/**
+ * USB control transfer.
+ */
+typedef struct dc_usb_control_t {
+ unsigned char bmRequestType;
+ unsigned char bRequest;
+ unsigned short wValue;
+ unsigned short wIndex;
+ unsigned short wLength;
+} dc_usb_control_t;
+
+/**
+ * Endpoint direction bits of the USB control transfer.
+ */
+typedef enum dc_usb_endpoint_t {
+ DC_USB_ENDPOINT_OUT = 0x00,
+ DC_USB_ENDPOINT_IN = 0x80
+} dc_usb_endpoint_t;
+
+/**
+ * Request type bits of the USB control transfer.
+ */
+typedef enum dc_usb_request_t {
+ DC_USB_REQUEST_STANDARD = 0x00,
+ DC_USB_REQUEST_CLASS = 0x20,
+ DC_USB_REQUEST_VENDOR = 0x40,
+ DC_USB_REQUEST_RESERVED = 0x60
+} dc_usb_request_t;
+
+/**
+ * Recipient bits of the USB control transfer.
+ */
+typedef enum dc_usb_recipient_t {
+ DC_USB_RECIPIENT_DEVICE = 0x00,
+ DC_USB_RECIPIENT_INTERFACE = 0x01,
+ DC_USB_RECIPIENT_ENDPOINT = 0x02,
+ DC_USB_RECIPIENT_OTHER = 0x03,
+} dc_usb_recipient_t;
+
+/**
+ * Opaque object representing a USB device.
+ */
+typedef struct dc_usb_device_t dc_usb_device_t;
+
+/**
+ * Get the vendor id (VID) of the USB device.
+ *
+ * @param[in] device A valid USB device.
+ */
+unsigned int
+dc_usb_device_get_vid (dc_usb_device_t *device);
+
+/**
+ * Get the product id (PID) of the USB device.
+ *
+ * @param[in] device A valid USB device.
+ */
+unsigned int
+dc_usb_device_get_pid (dc_usb_device_t *device);
+
+/**
+ * Destroy the USB device and free all resources.
+ *
+ * @param[in] device A valid USB device.
+ */
+void
+dc_usb_device_free(dc_usb_device_t *device);
+
+/**
+ * Create an iterator to enumerate the USB devices.
+ *
+ * @param[out] iterator A location to store the iterator.
+ * @param[in] context A valid context object.
+ * @param[in] descriptor A valid device descriptor or NULL.
+ * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
+ * on failure.
+ */
+dc_status_t
+dc_usb_iterator_new (dc_iterator_t **iterator, dc_context_t *context, dc_descriptor_t *descriptor);
+
+/**
+ * Open a USB connection.
+ *
+ * @param[out] iostream A location to store the USB connection.
+ * @param[in] context A valid context object.
+ * @param[in] device A valid USB device.
+ * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
+ * on failure.
+ */
+dc_status_t
+dc_usb_open (dc_iostream_t **iostream, dc_context_t *context, dc_usb_device_t *device);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* DC_USB_H */
diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj
index 0580e56..5dcad41 100644
--- a/msvc/libdivecomputer.vcproj
+++ b/msvc/libdivecomputer.vcproj
@@ -518,6 +518,10 @@
RelativePath="..\src\timer.c"
>
+
+
@@ -872,6 +876,10 @@
RelativePath="..\include\libdivecomputer\units.h"
>
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 7b18286..b33acd0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -76,6 +76,7 @@ libdivecomputer_la_SOURCES = \
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
socket.h socket.c \
irda.c \
+ usb.c \
usbhid.c \
bluetooth.c \
custom.c
diff --git a/src/descriptor-private.h b/src/descriptor-private.h
index eaf7303..d829422 100644
--- a/src/descriptor-private.h
+++ b/src/descriptor-private.h
@@ -33,6 +33,12 @@ typedef struct dc_usb_desc_t {
unsigned short pid;
} dc_usb_desc_t;
+typedef struct dc_usb_params_t {
+ unsigned int interface;
+ unsigned char endpoint_in;
+ unsigned char endpoint_out;
+} dc_usb_params_t;
+
int
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params);
diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols
index b17d60b..85caae5 100644
--- a/src/libdivecomputer.symbols
+++ b/src/libdivecomputer.symbols
@@ -70,6 +70,12 @@ dc_irda_device_free
dc_irda_iterator_new
dc_irda_open
+dc_usb_device_get_vid
+dc_usb_device_get_pid
+dc_usb_device_free
+dc_usb_iterator_new
+dc_usb_open
+
dc_usbhid_device_get_vid
dc_usbhid_device_get_pid
dc_usbhid_device_free
diff --git a/src/usb.c b/src/usb.c
new file mode 100644
index 0000000..87a1061
--- /dev/null
+++ b/src/usb.c
@@ -0,0 +1,585 @@
+/*
+ * libdivecomputer
+ *
+ * Copyright (C) 2020 Jef Driesen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+#include
+
+#ifdef HAVE_LIBUSB
+#ifdef _WIN32
+#define NOGDI
+#endif
+#include
+#endif
+
+#include
+
+#include "common-private.h"
+#include "context-private.h"
+#include "iostream-private.h"
+#include "descriptor-private.h"
+#include "iterator-private.h"
+#include "platform.h"
+
+#define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_usb_vtable)
+
+typedef struct dc_usb_session_t {
+ size_t refcount;
+#ifdef HAVE_LIBUSB
+ libusb_context *handle;
+#endif
+} dc_usb_session_t;
+
+struct dc_usb_device_t {
+ unsigned short vid, pid;
+ dc_usb_session_t *session;
+#ifdef HAVE_LIBUSB
+ struct libusb_device *handle;
+ int interface;
+ unsigned char endpoint_in;
+ unsigned char endpoint_out;
+#endif
+};
+
+#ifdef HAVE_LIBUSB
+static dc_status_t dc_usb_iterator_next (dc_iterator_t *iterator, void *item);
+static dc_status_t dc_usb_iterator_free (dc_iterator_t *iterator);
+
+static dc_status_t dc_usb_set_timeout (dc_iostream_t *iostream, int timeout);
+static dc_status_t dc_usb_poll (dc_iostream_t *iostream, int timeout);
+static dc_status_t dc_usb_read (dc_iostream_t *iostream, void *data, size_t size, size_t *actual);
+static dc_status_t dc_usb_write (dc_iostream_t *iostream, const void *data, size_t size, size_t *actual);
+static dc_status_t dc_usb_ioctl (dc_iostream_t *iostream, unsigned int request, void *data, size_t size);
+static dc_status_t dc_usb_close (dc_iostream_t *iostream);
+
+typedef struct dc_usb_iterator_t {
+ dc_iterator_t base;
+ dc_descriptor_t *descriptor;
+ dc_usb_session_t *session;
+ struct libusb_device **devices;
+ size_t count;
+ size_t current;
+} dc_usb_iterator_t;
+
+typedef struct dc_usb_t {
+ /* Base class. */
+ dc_iostream_t base;
+ /* Internal state. */
+ dc_usb_session_t *session;
+ libusb_device_handle *handle;
+ int interface;
+ unsigned char endpoint_in;
+ unsigned char endpoint_out;
+ unsigned int timeout;
+} dc_usb_t;
+
+static const dc_iterator_vtable_t dc_usb_iterator_vtable = {
+ sizeof(dc_usb_iterator_t),
+ dc_usb_iterator_next,
+ dc_usb_iterator_free,
+};
+
+static const dc_iostream_vtable_t dc_usb_vtable = {
+ sizeof(dc_usb_t),
+ dc_usb_set_timeout, /* set_timeout */
+ NULL, /* set_break */
+ NULL, /* set_dtr */
+ NULL, /* set_rts */
+ NULL, /* get_lines */
+ NULL, /* get_available */
+ NULL, /* configure */
+ dc_usb_poll, /* poll */
+ dc_usb_read, /* read */
+ dc_usb_write, /* write */
+ dc_usb_ioctl, /* ioctl */
+ NULL, /* flush */
+ NULL, /* purge */
+ NULL, /* sleep */
+ dc_usb_close, /* close */
+};
+
+static dc_status_t
+syserror(int errcode)
+{
+ switch (errcode) {
+ case LIBUSB_ERROR_INVALID_PARAM:
+ return DC_STATUS_INVALIDARGS;
+ case LIBUSB_ERROR_NO_MEM:
+ return DC_STATUS_NOMEMORY;
+ case LIBUSB_ERROR_NO_DEVICE:
+ case LIBUSB_ERROR_NOT_FOUND:
+ return DC_STATUS_NODEVICE;
+ case LIBUSB_ERROR_ACCESS:
+ case LIBUSB_ERROR_BUSY:
+ return DC_STATUS_NOACCESS;
+ case LIBUSB_ERROR_TIMEOUT:
+ return DC_STATUS_TIMEOUT;
+ default:
+ return DC_STATUS_IO;
+ }
+}
+
+static dc_status_t
+dc_usb_session_new (dc_usb_session_t **out, dc_context_t *context)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_usb_session_t *session = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ session = (dc_usb_session_t *) malloc (sizeof(dc_usb_session_t));
+ if (session == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ status = DC_STATUS_NOMEMORY;
+ goto error_unlock;
+ }
+
+ session->refcount = 1;
+
+ int rc = libusb_init (&session->handle);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (context, "Failed to initialize usb support (%s).",
+ libusb_error_name (rc));
+ status = syserror (rc);
+ goto error_free;
+ }
+
+ *out = session;
+
+ return status;
+
+error_free:
+ free (session);
+error_unlock:
+ return status;
+}
+
+static dc_usb_session_t *
+dc_usb_session_ref (dc_usb_session_t *session)
+{
+ if (session == NULL)
+ return NULL;
+
+ session->refcount++;
+
+ return session;
+}
+
+static dc_status_t
+dc_usb_session_unref (dc_usb_session_t *session)
+{
+ if (session == NULL)
+ return DC_STATUS_SUCCESS;
+
+ if (--session->refcount == 0) {
+ libusb_exit (session->handle);
+ free (session);
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+#endif
+
+unsigned int
+dc_usb_device_get_vid (dc_usb_device_t *device)
+{
+ if (device == NULL)
+ return 0;
+
+ return device->vid;
+}
+
+unsigned int
+dc_usb_device_get_pid (dc_usb_device_t *device)
+{
+ if (device == NULL)
+ return 0;
+
+ return device->pid;
+}
+
+void
+dc_usb_device_free(dc_usb_device_t *device)
+{
+ if (device == NULL)
+ return;
+
+#ifdef HAVE_LIBUSB
+ libusb_unref_device (device->handle);
+ dc_usb_session_unref (device->session);
+#endif
+ free (device);
+}
+
+dc_status_t
+dc_usb_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_t *descriptor)
+{
+#ifdef HAVE_LIBUSB
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_usb_iterator_t *iterator = NULL;
+
+ if (out == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ iterator = (dc_usb_iterator_t *) dc_iterator_allocate (context, &dc_usb_iterator_vtable);
+ if (iterator == NULL) {
+ ERROR (context, "Failed to allocate memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Initialize the usb library.
+ status = dc_usb_session_new (&iterator->session, context);
+ if (status != DC_STATUS_SUCCESS) {
+ goto error_free;
+ }
+
+ // Enumerate the USB devices.
+ struct libusb_device **devices = NULL;
+ ssize_t ndevices = libusb_get_device_list (iterator->session->handle, &devices);
+ if (ndevices < 0) {
+ ERROR (context, "Failed to enumerate the usb devices (%s).",
+ libusb_error_name (ndevices));
+ status = syserror (ndevices);
+ goto error_session_unref;
+ }
+
+ iterator->devices = devices;
+ iterator->count = ndevices;
+ iterator->current = 0;
+ iterator->descriptor = descriptor;
+
+ *out = (dc_iterator_t *) iterator;
+
+ return DC_STATUS_SUCCESS;
+
+error_session_unref:
+ dc_usb_session_unref (iterator->session);
+error_free:
+ dc_iterator_deallocate ((dc_iterator_t *) iterator);
+ return status;
+#else
+ return DC_STATUS_UNSUPPORTED;
+#endif
+}
+
+#ifdef HAVE_LIBUSB
+static dc_status_t
+dc_usb_iterator_next (dc_iterator_t *abstract, void *out)
+{
+ dc_usb_iterator_t *iterator = (dc_usb_iterator_t *) abstract;
+ dc_usb_device_t *device = NULL;
+
+ while (iterator->current < iterator->count) {
+ struct libusb_device *current = iterator->devices[iterator->current++];
+
+ // Get the device descriptor.
+ struct libusb_device_descriptor dev;
+ int rc = libusb_get_device_descriptor (current, &dev);
+ if (rc < 0) {
+ ERROR (abstract->context, "Failed to get the device descriptor (%s).",
+ libusb_error_name (rc));
+ return syserror (rc);
+ }
+
+ dc_usb_desc_t usb = {dev.idVendor, dev.idProduct};
+ dc_usb_params_t params = {0, 0, 0};
+ if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USB, &usb, ¶ms)) {
+ continue;
+ }
+
+ // Get the active configuration descriptor.
+ struct libusb_config_descriptor *config = NULL;
+ rc = libusb_get_active_config_descriptor (current, &config);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (abstract->context, "Failed to get the configuration descriptor (%s).",
+ libusb_error_name (rc));
+ return syserror (rc);
+ }
+
+ // Find the first matching interface.
+ const struct libusb_interface_descriptor *interface = NULL;
+ for (unsigned int i = 0; i < config->bNumInterfaces; i++) {
+ const struct libusb_interface *iface = &config->interface[i];
+ for (int j = 0; j < iface->num_altsetting; j++) {
+ const struct libusb_interface_descriptor *desc = &iface->altsetting[j];
+ if (interface == NULL && desc->bInterfaceNumber == params.interface) {
+ interface = desc;
+ }
+ }
+ }
+
+ if (interface == NULL) {
+ libusb_free_config_descriptor (config);
+ continue;
+ }
+
+ // Find the first matching input and output bulk endpoints.
+ const struct libusb_endpoint_descriptor *ep_in = NULL, *ep_out = NULL;
+ for (unsigned int i = 0; i < interface->bNumEndpoints; i++) {
+ const struct libusb_endpoint_descriptor *desc = &interface->endpoint[i];
+
+ unsigned int type = desc->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK;
+ unsigned int direction = desc->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK;
+
+ if (type != LIBUSB_TRANSFER_TYPE_BULK) {
+ continue;
+ }
+
+ if (ep_in == NULL && direction == LIBUSB_ENDPOINT_IN &&
+ (params.endpoint_in == 0 || params.endpoint_in == desc->bEndpointAddress)) {
+ ep_in = desc;
+ }
+
+ if (ep_out == NULL && direction == LIBUSB_ENDPOINT_OUT &&
+ (params.endpoint_out == 0 || params.endpoint_out == desc->bEndpointAddress)) {
+ ep_out = desc;
+ }
+ }
+
+ if (ep_in == NULL || ep_out == NULL) {
+ libusb_free_config_descriptor (config);
+ continue;
+ }
+
+ device = (dc_usb_device_t *) malloc (sizeof(dc_usb_device_t));
+ if (device == NULL) {
+ ERROR (abstract->context, "Failed to allocate memory.");
+ libusb_free_config_descriptor (config);
+ return DC_STATUS_NOMEMORY;
+ }
+
+ device->session = dc_usb_session_ref (iterator->session);
+ device->vid = dev.idVendor;
+ device->pid = dev.idProduct;
+ device->handle = libusb_ref_device (current);
+ device->interface = interface->bInterfaceNumber;
+ device->endpoint_in = ep_in->bEndpointAddress;
+ device->endpoint_out = ep_out->bEndpointAddress;
+
+ *(dc_usb_device_t **) out = device;
+
+ libusb_free_config_descriptor (config);
+
+ return DC_STATUS_SUCCESS;
+ }
+
+ return DC_STATUS_DONE;
+}
+
+static dc_status_t
+dc_usb_iterator_free (dc_iterator_t *abstract)
+{
+ dc_usb_iterator_t *iterator = (dc_usb_iterator_t *) abstract;
+
+ libusb_free_device_list (iterator->devices, 1);
+ dc_usb_session_unref (iterator->session);
+
+ return DC_STATUS_SUCCESS;
+}
+#endif
+
+dc_status_t
+dc_usb_open (dc_iostream_t **out, dc_context_t *context, dc_usb_device_t *device)
+{
+#ifdef HAVE_LIBUSB
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_usb_t *usb = NULL;
+
+ if (out == NULL || device == NULL)
+ return DC_STATUS_INVALIDARGS;
+
+ INFO (context, "Open: vid=%04x, pid=%04x, interface=%u, endpoints=%02x,%02x",
+ device->vid, device->pid, device->interface, device->endpoint_in, device->endpoint_out);
+
+ // Allocate memory.
+ usb = (dc_usb_t *) dc_iostream_allocate (context, &dc_usb_vtable, DC_TRANSPORT_USB);
+ if (usb == NULL) {
+ ERROR (context, "Out of memory.");
+ return DC_STATUS_NOMEMORY;
+ }
+
+ // Initialize the usb library.
+ usb->session = dc_usb_session_ref (device->session);
+ if (usb->session == NULL) {
+ goto error_free;
+ }
+
+ // Open the USB device.
+ int rc = libusb_open (device->handle, &usb->handle);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (context, "Failed to open the usb device (%s).",
+ libusb_error_name (rc));
+ status = syserror (rc);
+ goto error_session_unref;
+ }
+
+#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102)
+ libusb_set_auto_detach_kernel_driver (usb->handle, 1);
+#endif
+
+ // Claim the interface.
+ rc = libusb_claim_interface (usb->handle, device->interface);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (context, "Failed to claim the usb interface (%s).",
+ libusb_error_name (rc));
+ status = syserror (rc);
+ goto error_usb_close;
+ }
+
+ usb->interface = device->interface;
+ usb->endpoint_in = device->endpoint_in;
+ usb->endpoint_out = device->endpoint_out;
+ usb->timeout = 0;
+
+ *out = (dc_iostream_t *) usb;
+
+ return DC_STATUS_SUCCESS;
+
+error_usb_close:
+ libusb_close (usb->handle);
+error_session_unref:
+ dc_usb_session_unref (usb->session);
+error_free:
+ dc_iostream_deallocate ((dc_iostream_t *) usb);
+ return status;
+#else
+ return DC_STATUS_UNSUPPORTED;
+#endif
+}
+
+#ifdef HAVE_LIBUSB
+static dc_status_t
+dc_usb_close (dc_iostream_t *abstract)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_usb_t *usb = (dc_usb_t *) abstract;
+
+ libusb_release_interface (usb->handle, usb->interface);
+ libusb_close (usb->handle);
+ dc_usb_session_unref (usb->session);
+
+ return status;
+}
+
+static dc_status_t
+dc_usb_set_timeout (dc_iostream_t *abstract, int timeout)
+{
+ dc_usb_t *usb = (dc_usb_t *) abstract;
+
+ if (timeout < 0) {
+ usb->timeout = 0;
+ } else if (timeout == 0) {
+ return DC_STATUS_UNSUPPORTED;
+ } else {
+ usb->timeout = timeout;
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+dc_usb_poll (dc_iostream_t *abstract, int timeout)
+{
+ return DC_STATUS_UNSUPPORTED;
+}
+
+static dc_status_t
+dc_usb_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_usb_t *usb = (dc_usb_t *) abstract;
+ int nbytes = 0;
+
+ int rc = libusb_bulk_transfer (usb->handle, usb->endpoint_in, data, size, &nbytes, usb->timeout);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (abstract->context, "Usb read bulk transfer failed (%s).",
+ libusb_error_name (rc));
+ status = syserror (rc);
+ goto out;
+ }
+
+out:
+ if (actual)
+ *actual = nbytes;
+
+ return status;
+}
+
+static dc_status_t
+dc_usb_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
+{
+ dc_status_t status = DC_STATUS_SUCCESS;
+ dc_usb_t *usb = (dc_usb_t *) abstract;
+ int nbytes = 0;
+
+ int rc = libusb_bulk_transfer (usb->handle, usb->endpoint_out, (void *) data, size, &nbytes, 0);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (abstract->context, "Usb write bulk transfer failed (%s).",
+ libusb_error_name (rc));
+ status = syserror (rc);
+ goto out;
+ }
+
+out:
+ if (actual)
+ *actual = nbytes;
+
+ return status;
+}
+
+static dc_status_t
+dc_usb_ioctl_control (dc_iostream_t *abstract, void *data, size_t size)
+{
+ dc_usb_t *usb = (dc_usb_t *) abstract;
+ const dc_usb_control_t *control = (const dc_usb_control_t *) data;
+
+ if (size < sizeof(control) || control->wLength > size - sizeof(control)) {
+ return DC_STATUS_INVALIDARGS;
+ }
+
+ int rc = libusb_control_transfer (usb->handle,
+ control->bmRequestType, control->bRequest, control->wValue, control->wIndex,
+ (unsigned char *) data + sizeof(control), control->wLength, usb->timeout);
+ if (rc != LIBUSB_SUCCESS) {
+ ERROR (abstract->context, "Usb control transfer failed (%s).",
+ libusb_error_name (rc));
+ return syserror (rc);
+ }
+
+ return DC_STATUS_SUCCESS;
+}
+
+static dc_status_t
+dc_usb_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size)
+{
+ switch (request) {
+ case DC_IOCTL_USB_CONTROL_READ:
+ case DC_IOCTL_USB_CONTROL_WRITE:
+ return dc_usb_ioctl_control (abstract, data, size);
+ default:
+ return DC_STATUS_UNSUPPORTED;
+ }
+}
+#endif