diff --git a/configure.ac b/configure.ac index 0391cb2..1d3c5d8 100644 --- a/configure.ac +++ b/configure.ac @@ -73,26 +73,36 @@ esac AC_MSG_RESULT([$os_win32]) AM_CONDITIONAL([OS_WIN32], [test "$os_win32" = "yes"]) +DEPENDENCIES="" + # Checks for USB support. -AC_ARG_ENABLE([libusb], AS_HELP_STRING([--disable-libusb], [Do not look for libusb])) -AS_IF([test "x$enable_libusb" != "xno"], [ -PKG_CHECK_MODULES([LIBUSB], [libusb-1.0], [have_libusb=yes], [have_libusb=no]) -if test "$have_libusb" = "yes"; then - AC_DEFINE([HAVE_LIBUSB], [1], [libusb support]) - AC_SUBST([DEPENDENCIES], [libusb-1.0]) -fi +AC_ARG_WITH([libusb], + [AS_HELP_STRING([--without-libusb], + [Build without the libusb library])], + [], [with_libusb=auto]) +AS_IF([test "x$with_libusb" != "xno"], [ + PKG_CHECK_MODULES([LIBUSB], [libusb-1.0], [have_libusb=yes], [have_libusb=no]) + AS_IF([test "x$have_libusb" = "xyes"], [ + AC_DEFINE([HAVE_LIBUSB], [1], [libusb library]) + DEPENDENCIES="$DEPENDENCIES libusb-1.0" + ]) ]) -# Checks for hidapi support. -AC_ARG_ENABLE([hidapi], AS_HELP_STRING([--disable-hidapi], [Do not look for hidapi])) -AS_IF([test "x$enable_hidapi" != "xno"], [ -PKG_CHECK_MODULES([HIDAPI], [hidapi], [have_hidapi=yes], [have_hidapi=no]) -if test "$have_hidapi" = "yes"; then - AC_DEFINE([HAVE_HIDAPI], [1], [hidapi support]) - AC_SUBST([DEPENDENCIES], [hidapi]) -fi +# Checks for HIDAPI support. +AC_ARG_WITH([hidapi], + [AS_HELP_STRING([--without-hidapi], + [Build without the hidapi library])], + [], [with_hidapi=auto]) +AS_IF([test "x$with_hidapi" != "xno"], [ + PKG_CHECK_MODULES([HIDAPI], [hidapi], [have_hidapi=yes], [have_hidapi=no]) + AS_IF([test "x$have_hidapi" = "xyes"], [ + AC_DEFINE([HAVE_HIDAPI], [1], [hidapi library]) + DEPENDENCIES="$DEPENDENCIES hidapi" + ]) ]) +AC_SUBST([DEPENDENCIES]) + # Checks for IrDA support. AC_CHECK_HEADERS([winsock2.h af_irda.h], [irda_win32=yes], [irda_win32=no], [ #if HAVE_WINSOCK2_H diff --git a/src/Makefile.am b/src/Makefile.am index eba4151..fba41a8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -83,6 +83,8 @@ else libdivecomputer_la_SOURCES += irda.h irda_dummy.c endif +libdivecomputer_la_SOURCES += usbhid.h usbhid.c + if OS_WIN32 libdivecomputer_la_SOURCES += libdivecomputer.rc endif diff --git a/src/descriptor.c b/src/descriptor.c index 39b3161..0646f2f 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -23,6 +23,12 @@ #include "config.h" #endif +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) +#define USBHID +#elif defined(HAVE_HIDAPI) +#define USBHID +#endif + #include #include @@ -81,7 +87,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Suunto", "Vyper Novo", DC_FAMILY_SUUNTO_D9, 0x1D}, {"Suunto", "Zoop Novo", DC_FAMILY_SUUNTO_D9, 0x1E}, /* Suunto EON Steel */ -#ifdef HAVE_LIBUSB +#ifdef USBHID {"Suunto", "EON Steel", DC_FAMILY_SUUNTO_EONSTEEL, 0}, #endif /* Uwatec Aladin */ diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index e67099f..c230c35 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -159,7 +159,7 @@ static const hw_ostc_layout_t hw_ostc_layout_frog = { 19, /* temperature */ 34, /* battery volt after dive */ 23, /* desat */ - 32, /* firmware */ /* 32 or 34??? Anton says 32 (34 == battery), Jef says 34 */ + 32, /* firmware */ 49, /* deco_info1 */ 50, /* deco_info2 */ 51, /* decomode */ diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index 4d88ee3..50f800b 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -19,10 +19,6 @@ * MA 02110-1301 USA */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include #include #include @@ -32,31 +28,15 @@ #include "context-private.h" #include "device-private.h" #include "array.h" +#include "usbhid.h" #ifdef _MSC_VER #define snprintf _snprintf #endif -#if __APPLE__ && HAVE_HIDAPI -#include "hidapi/hidapi.h" -#endif -#if HAVE_LIBUSB || (__APPLE__ && HAVE_HIDAPI) - -#ifdef _WIN32 -#define NOGDI -#endif - -#include - typedef struct suunto_eonsteel_device_t { dc_device_t base; - - libusb_context *ctx; -#if __APPLE__ && HAVE_HIDAPI - hid_device *handle; -#else - libusb_device_handle *handle; -#endif + dc_usbhid_t *usbhid; unsigned int magic; unsigned short seq; unsigned char version[0x30]; @@ -150,18 +130,13 @@ static void put_le32(unsigned int val, unsigned char *p) static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer, int size) { unsigned char buf[64]; - const int InEndpoint = 0x82; - int rc, transferred, len; + dc_status_t rc = DC_STATUS_SUCCESS; + size_t transferred = 0; + int len; - /* 5000 = 5s timeout */ -#if __APPLE__ && HAVE_HIDAPI - transferred = hid_read_timeout(eon->handle, buf, PACKET_SIZE, 5000); - rc = (transferred <= 0) ? -1 : 0; -#else - rc = libusb_interrupt_transfer(eon->handle, InEndpoint, buf, PACKET_SIZE, &transferred, 5000); -#endif - if (rc) { - ERROR(eon->base.context, "read interrupt transfer failed (%s)", libusb_error_name(rc)); + rc = dc_usbhid_read(eon->usbhid, buf, PACKET_SIZE, &transferred); + if (rc != DC_STATUS_SUCCESS) { + ERROR(eon->base.context, "read interrupt transfer failed"); return -1; } if (transferred != PACKET_SIZE) { @@ -191,11 +166,11 @@ static int send_cmd(suunto_eonsteel_device_t *eon, unsigned int len, const unsigned char *buffer) { - const int OutEndpoint = 0x02; unsigned char buf[64]; - int transferred, rc; unsigned short seq = eon->seq; unsigned int magic = eon->magic; + dc_status_t rc = DC_STATUS_SUCCESS; + size_t transferred = 0; // Two-byte packet header, followed by 12 bytes of extended header if (len > sizeof(buf)-2-12) { @@ -225,14 +200,8 @@ static int send_cmd(suunto_eonsteel_device_t *eon, memcpy(buf+14, buffer, len); } -#if __APPLE__ && HAVE_HIDAPI - /* there is no write with timeout */ - transferred = hid_write(eon->handle, buf, PACKET_SIZE); - rc = (transferred <= 0) ? -1 : 0; -#else - rc = libusb_interrupt_transfer(eon->handle, OutEndpoint, buf, sizeof(buf), &transferred, 5000); -#endif - if (rc < 0) { + rc = dc_usbhid_write(eon->usbhid, buf, sizeof(buf), &transferred); + if (rc != DC_STATUS_SUCCESS) { ERROR(eon->base.context, "write interrupt transfer failed"); return -1; } @@ -543,27 +512,26 @@ static int get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry * static int initialize_eonsteel(suunto_eonsteel_device_t *eon) { - const int InEndpoint = 0x82; const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00}; unsigned char buf[64]; struct eon_hdr hdr; - /* Get rid of any pending stale input first */ - for (;;) { - int transferred; + dc_usbhid_set_timeout(eon->usbhid, 10); -#if __APPLE__ && HAVE_HIDAPI - transferred = hid_read_timeout(eon->handle, buf, sizeof(buf), 10); - int rc = (transferred <= 0) ? -1 : 0; -#else - int rc = libusb_interrupt_transfer(eon->handle, InEndpoint, buf, sizeof(buf), &transferred, 10); -#endif - if (rc < 0) + /* Get rid of any pending stale input first */ + /* NOTE! This will cause an annoying warning from dc_usbhid_read() */ + for (;;) { + size_t transferred = 0; + + dc_status_t rc = dc_usbhid_read(eon->usbhid, buf, sizeof(buf), &transferred); + if (rc != DC_STATUS_SUCCESS) break; if (!transferred) break; } + dc_usbhid_set_timeout(eon->usbhid, 5000); + if (send_cmd(eon, INIT_CMD, sizeof(init), init)) { ERROR(eon->base.context, "Failed to send initialization command"); return -1; @@ -599,67 +567,24 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, const char memset (eon->version, 0, sizeof (eon->version)); memset (eon->fingerprint, 0, sizeof (eon->fingerprint)); -#if __APPLE__ && HAVE_HIDAPI - - if (hid_init()) { - ERROR(context, "hid_init() failed"); - status = DC_STATUS_IO; + status = dc_usbhid_open(&eon->usbhid, context, 0x1493, 0x0030); + if (status != DC_STATUS_SUCCESS) { + ERROR(context, "unable to open device"); goto error_free; } - eon->handle = hid_open(0x1493, 0x0030, NULL); - if (!eon->handle) { - ERROR(context, "unable to open device"); - hid_exit(); - status = DC_STATUS_IO; - goto error_usb_exit; - } - -#else - - if (libusb_init(&eon->ctx)) { - ERROR(context, "libusb_init() failed"); - status = DC_STATUS_IO; - goto error_free; - } - - eon->handle = libusb_open_device_with_vid_pid(eon->ctx, 0x1493, 0x0030); - if (!eon->handle) { - ERROR(context, "unable to open device"); - status = DC_STATUS_IO; - goto error_usb_exit; - } - -#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) - libusb_set_auto_detach_kernel_driver(eon->handle, 1); -#endif - - libusb_claim_interface(eon->handle, 0); -#endif if (initialize_eonsteel(eon) < 0) { ERROR(context, "unable to initialize device"); status = DC_STATUS_IO; - goto error_usb_close; + goto error_close; } *out = (dc_device_t *) eon; return DC_STATUS_SUCCESS; -error_usb_close: -#if __APPLE__ && HAVE_HIDAPI - hid_close(eon->handle); -#else - libusb_close(eon->handle); -#endif - -error_usb_exit: -#if __APPLE__ && HAVE_HIDAPI - hid_exit(); -#else - libusb_exit(eon->ctx); -#endif - +error_close: + dc_usbhid_close(eon->usbhid); error_free: free(eon); return status; @@ -783,25 +708,7 @@ suunto_eonsteel_device_close(dc_device_t *abstract) { suunto_eonsteel_device_t *eon = (suunto_eonsteel_device_t *) abstract; - -#if __APPLE__ && HAVE_HIDAPI - hid_close(eon->handle); - hid_exit(); -#else - libusb_close(eon->handle); - libusb_exit(eon->ctx); -#endif + dc_usbhid_close(eon->usbhid); return DC_STATUS_SUCCESS; } - -#else // no LIBUSB support - -dc_status_t -suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, const char *name, unsigned int model) -{ - ERROR(context, "The Suunto EON Steel backend needs libusb-1.0"); - return DC_STATUS_UNSUPPORTED; -} - -#endif diff --git a/src/suunto_eonsteel_parser.c b/src/suunto_eonsteel_parser.c index fc1301d..4537b66 100644 --- a/src/suunto_eonsteel_parser.c +++ b/src/suunto_eonsteel_parser.c @@ -706,8 +706,6 @@ static void sample_event_notify_type(const struct type_desc *desc, struct sample info->notify_type = lookup_enum(desc, type); } - -// FIXME! This needs to parse the actual type descriptor enum static void sample_event_notify_value(const struct type_desc *desc, struct sample_data *info, unsigned char value) { dc_sample_value_t sample = {0}; @@ -754,7 +752,6 @@ static void sample_event_alarm_type(const struct type_desc *desc, struct sample_ } -// FIXME! This needs to parse the actual type descriptor enum static void sample_event_alarm_value(const struct type_desc *desc, struct sample_data *info, unsigned char value) { const char *name; diff --git a/src/usbhid.c b/src/usbhid.c new file mode 100644 index 0000000..44c0886 --- /dev/null +++ b/src/usbhid.c @@ -0,0 +1,420 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 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 + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) +#define USBHID +#ifdef _WIN32 +#define NOGDI +#endif +#include +#elif defined(HAVE_HIDAPI) +#define USBHID +#include +#endif + +#include "usbhid.h" +#include "common-private.h" +#include "context-private.h" + +struct dc_usbhid_t { + /* Library context. */ + dc_context_t *context; + /* Internal state. */ +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) + libusb_context *ctx; + libusb_device_handle *handle; + int interface; + unsigned char endpoint_in; + unsigned char endpoint_out; + unsigned int timeout; +#elif defined(HAVE_HIDAPI) + hid_device *handle; + int timeout; +#endif +}; + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) +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; + } +} +#endif + +dc_status_t +dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsigned int pid) +{ +#ifdef USBHID + dc_status_t status = DC_STATUS_SUCCESS; + dc_usbhid_t *usbhid = NULL; + int rc = 0; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (context, "Open: vid=%04x, pid=%04x", vid, pid); + + // Allocate memory. + usbhid = (dc_usbhid_t *) malloc (sizeof (dc_usbhid_t)); + if (usbhid == NULL) { + ERROR (context, "Out of memory."); + return DC_STATUS_NOMEMORY; + } + + // Library context. + usbhid->context = context; + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) + struct libusb_device **devices = NULL; + struct libusb_config_descriptor *config = NULL; + + // Initialize the libusb library. + rc = libusb_init (&usbhid->ctx); + if (rc != LIBUSB_SUCCESS) { + ERROR (context, "Failed to initialize usb support (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto error_free; + } + + // Enumerate the USB devices. + ssize_t ndevices = libusb_get_device_list (usbhid->ctx, &devices); + if (ndevices < 0) { + ERROR (context, "Failed to enumerate the usb devices (%s).", + libusb_error_name (ndevices)); + status = syserror (ndevices); + goto error_usb_exit; + } + + // Find the first device matching the VID/PID. + struct libusb_device *device = NULL; + for (size_t i = 0; i < ndevices; i++) { + struct libusb_device_descriptor desc; + rc = libusb_get_device_descriptor (devices[i], &desc); + if (rc < 0) { + ERROR (context, "Failed to get the device descriptor (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto error_usb_free_list; + } + + if (desc.idVendor == vid && desc.idProduct == pid) { + device = devices[i]; + break; + } + } + + if (device == NULL) { + ERROR (context, "No matching USB device found."); + status = DC_STATUS_NODEVICE; + goto error_usb_free_list; + } + + // Get the active configuration descriptor. + rc = libusb_get_active_config_descriptor (device, &config); + if (rc != LIBUSB_SUCCESS) { + ERROR (context, "Failed to get the configuration descriptor (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto error_usb_free_list; + } + + // Find the first HID 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 (unsigned int j = 0; j < iface->num_altsetting; j++) { + const struct libusb_interface_descriptor *desc = &iface->altsetting[j]; + if (desc->bInterfaceClass == LIBUSB_CLASS_HID && interface == NULL) { + interface = desc; + } + } + } + + if (interface == NULL) { + ERROR (context, "No HID interface found."); + status = DC_STATUS_IO; + goto error_usb_free_config; + } + + // Find the first input and output interrupt 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_INTERRUPT) + continue; + + if (direction == LIBUSB_ENDPOINT_IN && ep_in == NULL) { + ep_in = desc; + } + + if (direction == LIBUSB_ENDPOINT_OUT && ep_out == NULL) { + ep_out = desc; + } + } + + if (ep_in == NULL || ep_out == NULL) { + ERROR (context, "No interrupt endpoints found."); + status = DC_STATUS_IO; + goto error_usb_free_config; + } + + usbhid->interface = interface->bInterfaceNumber; + usbhid->endpoint_in = ep_in->bEndpointAddress; + usbhid->endpoint_out = ep_out->bEndpointAddress; + usbhid->timeout = 0; + + INFO (context, "Open: interface=%u, endpoints=%02x,%02x", + usbhid->interface, usbhid->endpoint_in, usbhid->endpoint_out); + + // Open the USB device. + rc = libusb_open (device, &usbhid->handle); + if (rc != LIBUSB_SUCCESS) { + ERROR (context, "Failed to open the usb device (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto error_usb_free_config; + } + +#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000102) + libusb_set_auto_detach_kernel_driver (usbhid->handle, 1); +#endif + + // Claim the HID interface. + rc = libusb_claim_interface (usbhid->handle, usbhid->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; + } + + libusb_free_config_descriptor (config); + libusb_free_device_list (devices, 1); + +#elif defined(HAVE_HIDAPI) + + // Initialize the hidapi library. + rc = hid_init(); + if (rc < 0) { + ERROR (context, "Failed to initialize usb support."); + status = DC_STATUS_IO; + goto error_free; + } + + // Open the USB device. + usbhid->handle = hid_open (vid, pid, NULL); + if (usbhid->handle == NULL) { + ERROR (context, "Failed to open the usb device."); + status = DC_STATUS_IO; + goto error_hid_exit; + } + + usbhid->timeout = -1; +#endif + + *out = usbhid; + + return DC_STATUS_SUCCESS; + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) +error_usb_close: + libusb_close (usbhid->handle); +error_usb_free_config: + libusb_free_config_descriptor (config); +error_usb_free_list: + libusb_free_device_list (devices, 1); +error_usb_exit: + libusb_exit (usbhid->ctx); +#elif defined(HAVE_HIDAPI) +error_hid_exit: + hid_exit (); +#endif +error_free: + free (usbhid); + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_usbhid_close (dc_usbhid_t *usbhid) +{ +#ifdef USBHID + dc_status_t status = DC_STATUS_SUCCESS; + + if (usbhid == NULL) + return DC_STATUS_SUCCESS; + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) + libusb_release_interface (usbhid->handle, usbhid->interface); + libusb_close (usbhid->handle); + libusb_exit (usbhid->ctx); +#elif defined(HAVE_HIDAPI) + hid_close(usbhid->handle); + hid_exit(); +#endif + free (usbhid); + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_usbhid_set_timeout (dc_usbhid_t *usbhid, int timeout) +{ +#ifdef USBHID + if (usbhid == NULL) + return DC_STATUS_INVALIDARGS; + + INFO (usbhid->context, "Timeout: value=%i", timeout); + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) + if (timeout < 0) { + usbhid->timeout = 0; + } else if (timeout == 0) { + return DC_STATUS_UNSUPPORTED; + } else { + usbhid->timeout = timeout; + } +#elif defined(HAVE_HIDAPI) + if (timeout < 0) { + usbhid->timeout = -1; + } else { + usbhid->timeout = timeout; + } +#endif + + return DC_STATUS_SUCCESS; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_usbhid_read (dc_usbhid_t *usbhid, void *data, size_t size, size_t *actual) +{ +#ifdef USBHID + dc_status_t status = DC_STATUS_SUCCESS; + int nbytes = 0; + + if (usbhid == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out; + } + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) + int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_in, data, size, &nbytes, usbhid->timeout); + if (rc != LIBUSB_SUCCESS) { + ERROR (usbhid->context, "Usb read interrupt transfer failed (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto out; + } +#elif defined(HAVE_HIDAPI) + nbytes = hid_read_timeout(usbhid->handle, data, size, usbhid->timeout); + if (nbytes < 0) { + ERROR (usbhid->context, "Usb read interrupt transfer failed."); + status = DC_STATUS_IO; + goto out; + } +#endif + +out: + HEXDUMP (usbhid->context, DC_LOGLEVEL_INFO, "Read", (unsigned char *) data, nbytes); + + if (actual) + *actual = nbytes; + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} + +dc_status_t +dc_usbhid_write (dc_usbhid_t *usbhid, const void *data, size_t size, size_t *actual) +{ +#ifdef USBHID + dc_status_t status = DC_STATUS_SUCCESS; + int nbytes = 0; + + if (usbhid == NULL) { + status = DC_STATUS_INVALIDARGS; + goto out; + } + +#if defined(HAVE_LIBUSB) && !defined(__APPLE__) + int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_out, (void *) data, size, &nbytes, 0); + if (rc != LIBUSB_SUCCESS) { + ERROR (usbhid->context, "Usb write interrupt transfer failed (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto out; + } +#elif defined(HAVE_HIDAPI) + nbytes = hid_write(usbhid->handle, data, size); + if (nbytes < 0) { + ERROR (usbhid->context, "Usb write interrupt transfer failed."); + status = DC_STATUS_IO; + goto out; + } +#endif + +out: + HEXDUMP (usbhid->context, DC_LOGLEVEL_INFO, "Write", (unsigned char *) data, nbytes); + + if (actual) + *actual = nbytes; + + return status; +#else + return DC_STATUS_UNSUPPORTED; +#endif +} diff --git a/src/usbhid.h b/src/usbhid.h new file mode 100644 index 0000000..65d98c1 --- /dev/null +++ b/src/usbhid.h @@ -0,0 +1,122 @@ +/* + * libdivecomputer + * + * Copyright (C) 2016 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_USBHID_H +#define DC_USBHID_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Opaque object representing a USB HID connection. + */ +typedef struct dc_usbhid_t dc_usbhid_t; + +/** + * Open a USB HID connection. + * + * @param[out] usbhid A location to store the USB HID connection. + * @param[in] context A valid context object. + * @param[in] vid The USB Vendor ID of the device. + * @param[in] pid The USB Product ID of the device. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_open (dc_usbhid_t **usbhid, dc_context_t *context, unsigned int vid, unsigned int pid); + +/** + * Close the connection and free all resources. + * + * @param[in] usbhid A valid USB HID connection. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_close (dc_usbhid_t *usbhid); + +/** + * Set the read timeout. + * + * There are three distinct modes available: + * + * 1. Blocking (timeout < 0): + * + * The read operation is blocked until all the requested bytes have + * been received. If the requested number of bytes does not arrive, + * the operation will block forever. + * + * 2. Non-blocking (timeout == 0): + * + * The read operation returns immediately with the bytes that have + * already been received, even if no bytes have been received. + * + * 3. Timeout (timeout > 0): + * + * The read operation is blocked until all the requested bytes have + * been received. If the requested number of bytes does not arrive + * within the specified amount of time, the operation will return + * with the bytes that have already been received. + * + * @param[in] usbhid A valid USB HID connection. + * @param[in] timeout The timeout in milliseconds. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_set_timeout (dc_usbhid_t *usbhid, int timeout); + +/** + * Read data from the USB HID connection. + * + * @param[in] usbhid A valid USB HID connection. + * @param[out] data The memory buffer to read the data into. + * @param[in] size The number of bytes to read. + * @param[out] actual An (optional) location to store the actual + * number of bytes transferred. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_read (dc_usbhid_t *usbhid, void *data, size_t size, size_t *actual); + +/** + * Write data to the USB HID connection. + * + * @param[in] usbhid A valid USB HID connection. + * @param[in] data The memory buffer to write the data from. + * @param[in] size The number of bytes to write. + * @param[out] actual An (optional) location to store the actual + * number of bytes transferred. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_usbhid_write (dc_usbhid_t *usbhid, const void *data, size_t size, size_t *actual); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_USBHID_H */