diff --git a/examples/common.c b/examples/common.c index cfabc03..d4e4ad4 100644 --- a/examples/common.c +++ b/examples/common.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "common.h" @@ -398,6 +399,41 @@ dctool_file_read (const char *filename) return buffer; } +static dc_status_t +dctool_usb_open (dc_iostream_t **out, dc_context_t *context, dc_descriptor_t *descriptor) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_iostream_t *iostream = NULL; + + // Discover the usb device. + dc_iterator_t *iterator = NULL; + dc_usb_device_t *device = NULL; + dc_usb_iterator_new (&iterator, context, descriptor); + while (dc_iterator_next (iterator, &device) == DC_STATUS_SUCCESS) { + break; + } + dc_iterator_free (iterator); + + if (device == NULL) { + ERROR ("No dive computer found."); + status = DC_STATUS_NODEVICE; + goto cleanup; + } + + // Open the usb device. + status = dc_usb_open (&iostream, context, device); + if (status != DC_STATUS_SUCCESS) { + ERROR ("Failed to open the usb device."); + goto cleanup; + } + + *out = iostream; + +cleanup: + dc_usb_device_free (device); + return status; +} + static dc_status_t dctool_usbhid_open (dc_iostream_t **out, dc_context_t *context, dc_descriptor_t *descriptor) { @@ -532,7 +568,7 @@ dctool_iostream_open (dc_iostream_t **iostream, dc_context_t *context, dc_descri case DC_TRANSPORT_SERIAL: return dc_serial_open (iostream, context, devname); case DC_TRANSPORT_USB: - return DC_STATUS_SUCCESS; + return dctool_usb_open(iostream, context, descriptor); case DC_TRANSPORT_USBHID: return dctool_usbhid_open(iostream, context, descriptor); case DC_TRANSPORT_IRDA: diff --git a/examples/dctool_scan.c b/examples/dctool_scan.c index b96d014..a68d46b 100644 --- a/examples/dctool_scan.c +++ b/examples/dctool_scan.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "dctool.h" @@ -59,6 +60,9 @@ scan (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t transpo case DC_TRANSPORT_BLUETOOTH: status = dc_bluetooth_iterator_new (&iterator, context, descriptor); break; + case DC_TRANSPORT_USB: + status = dc_usb_iterator_new (&iterator, context, descriptor); + break; case DC_TRANSPORT_USBHID: status = dc_usbhid_iterator_new (&iterator, context, descriptor); break; @@ -90,6 +94,10 @@ scan (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t transpo dc_bluetooth_device_get_name (device)); dc_bluetooth_device_free (device); break; + case DC_TRANSPORT_USB: + printf ("%04x:%04x\n", dc_usb_device_get_vid (device), dc_usb_device_get_pid (device)); + dc_usb_device_free (device); + break; case DC_TRANSPORT_USBHID: printf ("%04x:%04x\n", dc_usbhid_device_get_vid (device), dc_usbhid_device_get_pid (device)); dc_usbhid_device_free (device); 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/atomics_cobalt.c b/src/atomics_cobalt.c index e907b77..a67ef82 100644 --- a/src/atomics_cobalt.c +++ b/src/atomics_cobalt.c @@ -19,19 +19,10 @@ * MA 02110-1301 USA */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include // memcmp, memcpy #include // malloc, free -#ifdef HAVE_LIBUSB -#ifdef _WIN32 -#define NOGDI -#endif -#include -#endif +#include #include "atomics_cobalt.h" #include "context-private.h" @@ -41,8 +32,6 @@ #define ISINSTANCE(device) dc_device_isinstance((device), &atomics_cobalt_device_vtable) -#define EXITCODE(rc) (rc == LIBUSB_ERROR_TIMEOUT ? DC_STATUS_TIMEOUT : DC_STATUS_IO) - #define COBALT1 0 #define COBALT2 2 @@ -58,10 +47,7 @@ typedef struct atomics_cobalt_device_t { dc_device_t base; -#ifdef HAVE_LIBUSB - libusb_context *context; - libusb_device_handle *handle; -#endif + dc_iostream_t *iostream; unsigned int simulation; unsigned char fingerprint[6]; unsigned char version[SZ_VERSION]; @@ -69,7 +55,6 @@ typedef struct atomics_cobalt_device_t { static dc_status_t atomics_cobalt_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t atomics_cobalt_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); -static dc_status_t atomics_cobalt_device_close (dc_device_t *abstract); static const dc_device_vtable_t atomics_cobalt_device_vtable = { sizeof(atomics_cobalt_device_t), @@ -80,22 +65,19 @@ static const dc_device_vtable_t atomics_cobalt_device_vtable = { NULL, /* dump */ atomics_cobalt_device_foreach, /* foreach */ NULL, /* timesync */ - atomics_cobalt_device_close /* close */ + NULL /* close */ }; dc_status_t -atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context) +atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) { -#ifdef HAVE_LIBUSB dc_status_t status = DC_STATUS_SUCCESS; atomics_cobalt_device_t *device = NULL; -#endif if (out == NULL) return DC_STATUS_INVALIDARGS; -#ifdef HAVE_LIBUSB // Allocate memory. device = (atomics_cobalt_device_t *) dc_device_allocate (context, &atomics_cobalt_device_vtable); if (device == NULL) { @@ -104,67 +86,30 @@ atomics_cobalt_device_open (dc_device_t **out, dc_context_t *context) } // Set the default values. - device->context = NULL; - device->handle = NULL; + device->iostream = iostream; device->simulation = 0; memset (device->fingerprint, 0, sizeof (device->fingerprint)); - int rc = libusb_init (&device->context); - if (rc < 0) { - ERROR (context, "Failed to initialize usb support."); - status = DC_STATUS_IO; + // Set the timeout for receiving data (2000 ms). + status = dc_iostream_set_timeout (device->iostream, TIMEOUT); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the timeout."); goto error_free; } - device->handle = libusb_open_device_with_vid_pid (device->context, VID, PID); - if (device->handle == NULL) { - ERROR (context, "Failed to open the usb device."); - status = DC_STATUS_IO; - goto error_usb_exit; - } - - rc = libusb_claim_interface (device->handle, 0); - if (rc < 0) { - ERROR (context, "Failed to claim the usb interface."); - status = DC_STATUS_IO; - goto error_usb_close; - } - status = atomics_cobalt_device_version ((dc_device_t *) device, device->version, sizeof (device->version)); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to identify the dive computer."); - goto error_usb_close; + goto error_free; } *out = (dc_device_t*) device; return DC_STATUS_SUCCESS; -error_usb_close: - libusb_close (device->handle); -error_usb_exit: - libusb_exit (device->context); error_free: dc_device_deallocate ((dc_device_t *) device); return status; -#else - return DC_STATUS_UNSUPPORTED; -#endif -} - - -static dc_status_t -atomics_cobalt_device_close (dc_device_t *abstract) -{ - atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract; - -#ifdef HAVE_LIBUSB - libusb_release_interface(device->handle, 0); - libusb_close (device->handle); - libusb_exit (device->context); -#endif - - return DC_STATUS_SUCCESS; } @@ -202,6 +147,7 @@ atomics_cobalt_device_set_simulation (dc_device_t *abstract, unsigned int simula dc_status_t atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsigned int size) { + dc_status_t status = DC_STATUS_SUCCESS; atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract; if (!ISINSTANCE (abstract)) @@ -210,31 +156,31 @@ atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsi if (size < SZ_VERSION) return DC_STATUS_INVALIDARGS; -#ifdef HAVE_LIBUSB // Send the command to the dive computer. - uint8_t bRequest = 0x01; - int rc = libusb_control_transfer (device->handle, - LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, - bRequest, 0, 0, NULL, 0, TIMEOUT); - if (rc != LIBUSB_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); - return EXITCODE(rc); - } + unsigned char bRequest = 0x01; + dc_usb_control_t control = { + DC_USB_REQUEST_VENDOR | DC_USB_RECIPIENT_DEVICE | DC_USB_ENDPOINT_OUT, /* bmRequestType */ + bRequest, /* bRequest */ + 0, /* wValue */ + 0, /* wIndex */ + 0, /* wLength */ + }; - HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1); + status = dc_iostream_ioctl (device->iostream, DC_IOCTL_USB_CONTROL_WRITE, &control, sizeof(control)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } // Receive the answer from the dive computer. - int length = 0; + size_t length = 0; unsigned char packet[SZ_VERSION + 2] = {0}; - rc = libusb_bulk_transfer (device->handle, 0x82, - packet, sizeof (packet), &length, TIMEOUT); - if (rc != LIBUSB_SUCCESS || length != sizeof (packet)) { + status = dc_iostream_read (device->iostream, packet, sizeof(packet), &length); + if (status != DC_STATUS_SUCCESS || length != sizeof (packet)) { ERROR (abstract->context, "Failed to receive the answer."); - return EXITCODE(rc); + return status; } - HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length); - // Verify the checksum of the packet. unsigned short crc = array_uint16_le (packet + SZ_VERSION); unsigned short ccrc = checksum_add_uint16 (packet, SZ_VERSION, 0x0); @@ -246,16 +192,13 @@ atomics_cobalt_device_version (dc_device_t *abstract, unsigned char data[], unsi memcpy (data, packet, SZ_VERSION); return DC_STATUS_SUCCESS; -#else - return DC_STATUS_UNSUPPORTED; -#endif } static dc_status_t atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc_event_progress_t *progress) { -#ifdef HAVE_LIBUSB + dc_status_t status = DC_STATUS_SUCCESS; atomics_cobalt_device_t *device = (atomics_cobalt_device_t *) abstract; if (device_is_cancelled (abstract)) @@ -268,35 +211,37 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, } // Send the command to the dive computer. - uint8_t bRequest = 0; + unsigned char bRequest = 0; if (device->simulation) bRequest = init ? 0x02 : 0x03; else bRequest = init ? 0x09 : 0x0A; - int rc = libusb_control_transfer (device->handle, - LIBUSB_RECIPIENT_DEVICE | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT, - bRequest, 0, 0, NULL, 0, TIMEOUT); - if (rc != LIBUSB_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); - return EXITCODE(rc); - } - HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Write", &bRequest, 1); + dc_usb_control_t control = { + DC_USB_REQUEST_VENDOR | DC_USB_RECIPIENT_DEVICE | DC_USB_ENDPOINT_OUT, /* bmRequestType */ + bRequest, /* bRequest */ + 0, /* wValue */ + 0, /* wIndex */ + 0, /* wLength */ + }; + + status = dc_iostream_ioctl (device->iostream, DC_IOCTL_USB_CONTROL_WRITE, &control, sizeof(control)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } unsigned int nbytes = 0; while (1) { // Receive the answer from the dive computer. - int length = 0; + size_t length = 0; unsigned char packet[8 * 1024] = {0}; - rc = libusb_bulk_transfer (device->handle, 0x82, - packet, sizeof (packet), &length, TIMEOUT); - if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_TIMEOUT) { + status = dc_iostream_read (device->iostream, packet, sizeof(packet), &length); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_TIMEOUT) { ERROR (abstract->context, "Failed to receive the answer."); - return EXITCODE(rc); + return status; } - HEXDUMP (abstract->context, DC_LOGLEVEL_INFO, "Read", packet, length); - // Update and emit a progress event. if (progress) { progress->current += length; @@ -343,9 +288,6 @@ atomics_cobalt_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc_buffer_slice (buffer, 0, nbytes - 2); return DC_STATUS_SUCCESS; -#else - return DC_STATUS_UNSUPPORTED; -#endif } diff --git a/src/atomics_cobalt.h b/src/atomics_cobalt.h index 0308350..09f0226 100644 --- a/src/atomics_cobalt.h +++ b/src/atomics_cobalt.h @@ -23,6 +23,7 @@ #define ATOMICS_COBALT_H #include +#include #include #include #include @@ -32,7 +33,7 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -atomics_cobalt_device_open (dc_device_t **device, dc_context_t *context); +atomics_cobalt_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); dc_status_t atomics_cobalt_parser_create (dc_parser_t **parser, dc_context_t *context); diff --git a/src/bluetooth.c b/src/bluetooth.c index 282a31a..1889bf7 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -79,7 +79,7 @@ static dc_status_t dc_bluetooth_iterator_free (dc_iterator_t *iterator); typedef struct dc_bluetooth_iterator_t { dc_iterator_t base; - dc_filter_t filter; + dc_descriptor_t *descriptor; #ifdef _WIN32 HANDLE hLookup; #else @@ -376,7 +376,7 @@ dc_bluetooth_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descri iterator->count = ndevices; iterator->current = 0; #endif - iterator->filter = dc_descriptor_get_filter (descriptor); + iterator->descriptor = descriptor; *out = (dc_iterator_t *) iterator; @@ -456,7 +456,7 @@ dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out) INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", address, name ? name : ""); - if (iterator->filter && !iterator->filter (DC_TRANSPORT_BLUETOOTH, name)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_BLUETOOTH, name, NULL)) { continue; } diff --git a/src/descriptor-private.h b/src/descriptor-private.h index 5023f7f..d829422 100644 --- a/src/descriptor-private.h +++ b/src/descriptor-private.h @@ -33,10 +33,14 @@ typedef struct dc_usb_desc_t { unsigned short pid; } dc_usb_desc_t; -typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata); +typedef struct dc_usb_params_t { + unsigned int interface; + unsigned char endpoint_in; + unsigned char endpoint_out; +} dc_usb_params_t; -dc_filter_t -dc_descriptor_get_filter (dc_descriptor_t *descriptor); +int +dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params); #ifdef __cplusplus } diff --git a/src/descriptor.c b/src/descriptor.c index 7f2de39..cc8bb9a 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -36,19 +36,32 @@ values, \ C_ARRAY_SIZE(values) - isnullterminated, \ C_ARRAY_ITEMSIZE(values), \ - match) + match, \ + NULL, NULL, 0) + +#define DC_FILTER_INTERNAL_WITH_PARAMS(key, values, isnullterminated, match, params_dst, params_src) \ + dc_filter_internal( \ + key, \ + values, \ + C_ARRAY_SIZE(values) - isnullterminated, \ + C_ARRAY_ITEMSIZE(values), \ + match, \ + params_dst, params_src, sizeof *(params_src)) typedef int (*dc_match_t)(const void *, const void *); -static int dc_filter_uwatec (dc_transport_t transport, const void *userdata); -static int dc_filter_suunto (dc_transport_t transport, const void *userdata); -static int dc_filter_shearwater (dc_transport_t transport, const void *userdata); -static int dc_filter_hw (dc_transport_t transport, const void *userdata); -static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata); -static int dc_filter_mares (dc_transport_t transport, const void *userdata); -static int dc_filter_divesystem (dc_transport_t transport, const void *userdata); -static int dc_filter_oceanic (dc_transport_t transport, const void *userdata); -static int dc_filter_mclean (dc_transport_t transport, const void *userdata); +typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata, void *params); + +static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_mclean (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -314,8 +327,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Dive Rite", "NiTek Trio", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL}, {"Scubapro", "XTender 5", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL}, /* Atomic Aquatics Cobalt */ - {"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0, DC_TRANSPORT_USB, NULL}, - {"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2, DC_TRANSPORT_USB, NULL}, + {"Atomic Aquatics", "Cobalt", DC_FAMILY_ATOMICS_COBALT, 0, DC_TRANSPORT_USB, dc_filter_atomic}, + {"Atomic Aquatics", "Cobalt 2", DC_FAMILY_ATOMICS_COBALT, 2, DC_TRANSPORT_USB, dc_filter_atomic}, /* Shearwater Predator */ {"Shearwater", "Predator", DC_FAMILY_SHEARWATER_PREDATOR, 2, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_shearwater}, /* Shearwater Petrel */ @@ -461,13 +474,16 @@ dc_match_oceanic (const void *key, const void *value) } static int -dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match) +dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match, void *params_dst, const void *params_src, size_t params_size) { if (key == NULL) return 0; for (size_t i = 0; i < count; ++i) { if (match (key, (const unsigned char *) values + i * size)) { + if (params_src && params_dst) { + memcpy (params_dst, params_src, params_size); + } return 1; } } @@ -482,7 +498,7 @@ static const char * const rfcomm[] = { NULL }; -static int dc_filter_uwatec (dc_transport_t transport, const void *userdata) +static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params) { static const char * const irda[] = { "Aladin Smart Com", @@ -517,7 +533,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_suunto (dc_transport_t transport, const void *userdata) +static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params) { static const dc_usb_desc_t usbhid[] = { {0x1493, 0x0030}, // Eon Steel @@ -539,7 +555,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_hw (dc_transport_t transport, const void *userdata) +static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params) { static const char * const bluetooth[] = { "OSTC", @@ -555,7 +571,7 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_shearwater (dc_transport_t transport, const void *userdata) +static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params) { static const char * const bluetooth[] = { "Predator", @@ -575,7 +591,7 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata) +static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params) { static const char * const bluetooth[] = { "DiveComputer", @@ -590,7 +606,7 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_mares (dc_transport_t transport, const void *userdata) +static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params) { static const char * const bluetooth[] = { "Mares bluelink pro", @@ -604,7 +620,7 @@ static int dc_filter_mares (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_divesystem (dc_transport_t transport, const void *userdata) +static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params) { static const char * const bluetooth[] = { "DS", @@ -617,7 +633,7 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_oceanic (dc_transport_t transport, const void *userdata) +static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params) { static const unsigned int model[] = { 0x4552, // Oceanic Pro Plus X @@ -639,7 +655,7 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata) return 1; } -static int dc_filter_mclean(dc_transport_t transport, const void *userdata) +static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void *params) { static const char * const bluetooth[] = { "McLean Extreme", @@ -654,6 +670,23 @@ static int dc_filter_mclean(dc_transport_t transport, const void *userdata) return 1; } +static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params) +{ + static const dc_usb_desc_t usb[] = { + {0x0471, 0x0888}, // Atomic Aquatics Cobalt + }; + + static const dc_usb_params_t usb_params = { + 0, 0x82, 0x02 + }; + + if (transport == DC_TRANSPORT_USB) { + return DC_FILTER_INTERNAL_WITH_PARAMS (userdata, usb, 0, dc_match_usb, params, &usb_params); + } + + return 1; +} + dc_status_t dc_descriptor_iterator (dc_iterator_t **out) { @@ -745,11 +778,11 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor) return descriptor->transports; } -dc_filter_t -dc_descriptor_get_filter (dc_descriptor_t *descriptor) +int +dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params) { - if (descriptor == NULL) - return NULL; + if (descriptor == NULL || descriptor->filter == NULL) + return 1; - return descriptor->filter; + return descriptor->filter (transport, userdata, params); } diff --git a/src/device.c b/src/device.c index 271c2f4..62914dc 100644 --- a/src/device.c +++ b/src/device.c @@ -190,7 +190,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = zeagle_n2ition3_device_open (&device, context, iostream); break; case DC_FAMILY_ATOMICS_COBALT: - rc = atomics_cobalt_device_open (&device, context); + rc = atomics_cobalt_device_open (&device, context, iostream); break; case DC_FAMILY_SHEARWATER_PREDATOR: rc = shearwater_predator_device_open (&device, context, iostream); diff --git a/src/irda.c b/src/irda.c index 214e39f..448c57c 100644 --- a/src/irda.c +++ b/src/irda.c @@ -212,8 +212,6 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_ S_CLOSE (fd); dc_socket_exit (context); - dc_filter_t filter = dc_descriptor_get_filter (descriptor); - unsigned int count = 0; #ifdef _WIN32 for (size_t i = 0; i < list->numDevice; ++i) { @@ -233,7 +231,7 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_ INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", address, name, charset, hints); - if (filter && !filter (DC_TRANSPORT_IRDA, name)) { + if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name, NULL)) { continue; } 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/serial_posix.c b/src/serial_posix.c index 897b428..4fbcb96 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -88,7 +88,7 @@ struct dc_serial_device_t { typedef struct dc_serial_iterator_t { dc_iterator_t base; - dc_filter_t filter; + dc_descriptor_t *descriptor; DIR *dp; } dc_serial_iterator_t; @@ -189,7 +189,7 @@ dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto goto error_free; } - iterator->filter = dc_descriptor_get_filter (descriptor); + iterator->descriptor = descriptor; *out = (dc_iterator_t *) iterator; @@ -230,7 +230,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out) return DC_STATUS_NOMEMORY; } - if (iterator->filter && !iterator->filter (DC_TRANSPORT_SERIAL, filename)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, filename, NULL)) { continue; } diff --git a/src/serial_win32.c b/src/serial_win32.c index 3cb10ff..8427a25 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -57,7 +57,7 @@ struct dc_serial_device_t { typedef struct dc_serial_iterator_t { dc_iterator_t base; - dc_filter_t filter; + dc_descriptor_t *descriptor; HKEY hKey; DWORD count; DWORD current; @@ -180,7 +180,7 @@ dc_serial_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto } } - iterator->filter = dc_descriptor_get_filter (descriptor); + iterator->descriptor = descriptor; iterator->hKey = hKey; iterator->count = count; iterator->current = 0; @@ -226,7 +226,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out) // Null terminate the string. data[data_len] = 0; - if (iterator->filter && !iterator->filter (DC_TRANSPORT_SERIAL, data)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, data, NULL)) { continue; } 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 diff --git a/src/usbhid.c b/src/usbhid.c index e26d42b..b16bcc3 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -102,7 +102,7 @@ static dc_status_t dc_usbhid_close (dc_iostream_t *iostream); typedef struct dc_usbhid_iterator_t { dc_iterator_t base; - dc_filter_t filter; + dc_descriptor_t *descriptor; dc_usbhid_session_t *session; #if defined(USE_LIBUSB) struct libusb_device **devices; @@ -398,7 +398,7 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto iterator->devices = devices; iterator->current = devices; #endif - iterator->filter = dc_descriptor_get_filter (descriptor); + iterator->descriptor = descriptor; *out = (dc_iterator_t *) iterator; @@ -435,7 +435,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) } dc_usb_desc_t usb = {dev.idVendor, dev.idProduct}; - if (iterator->filter && !iterator->filter (DC_TRANSPORT_USBHID, &usb)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USBHID, &usb, NULL)) { continue; } @@ -518,7 +518,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) iterator->current = current->next; dc_usb_desc_t usb = {current->vendor_id, current->product_id}; - if (iterator->filter && !iterator->filter (DC_TRANSPORT_USBHID, &usb)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USBHID, &usb)) { continue; }