From edacbb2f13d12f113642b18e278454a7404abd2d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 29 Apr 2020 18:38:10 +0200 Subject: [PATCH 1/5] Disable direct access to the filter function Replace the small helper function to retrieve the function pointer and then call the function, with another helper function to call the filter function directly. This way the function pointer doesn't need to be exposed at all. --- src/bluetooth.c | 6 +++--- src/descriptor-private.h | 6 ++---- src/descriptor.c | 12 +++++++----- src/irda.c | 4 +--- src/serial_posix.c | 6 +++--- src/serial_win32.c | 6 +++--- src/usbhid.c | 8 ++++---- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/bluetooth.c b/src/bluetooth.c index 282a31a..e6fafd5 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)) { continue; } diff --git a/src/descriptor-private.h b/src/descriptor-private.h index 5023f7f..06c2980 100644 --- a/src/descriptor-private.h +++ b/src/descriptor-private.h @@ -33,10 +33,8 @@ 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); - -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); #ifdef __cplusplus } diff --git a/src/descriptor.c b/src/descriptor.c index 7f2de39..183d3d5 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -40,6 +40,8 @@ typedef int (*dc_match_t)(const void *, const void *); +typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata); + 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); @@ -745,11 +747,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) { - if (descriptor == NULL) - return NULL; + if (descriptor == NULL || descriptor->filter == NULL) + return 1; - return descriptor->filter; + return descriptor->filter (transport, userdata); } diff --git a/src/irda.c b/src/irda.c index 214e39f..89b7e50 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)) { continue; } diff --git a/src/serial_posix.c b/src/serial_posix.c index 897b428..cca501e 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)) { continue; } diff --git a/src/serial_win32.c b/src/serial_win32.c index 3cb10ff..b51aafd 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)) { continue; } diff --git a/src/usbhid.c b/src/usbhid.c index e26d42b..9ed6648 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)) { 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; } From 57f0ce6d7902117cfc4d0d6b401b516fc93ca488 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 29 Apr 2020 18:56:13 +0200 Subject: [PATCH 2/5] Add support for filter parameters The filter parameter provides a mechanism to pass some additional information, needed to configure the I/O stream, back to the caller. --- src/bluetooth.c | 2 +- src/descriptor-private.h | 2 +- src/descriptor.c | 59 ++++++++++++++++++++++++---------------- src/irda.c | 2 +- src/serial_posix.c | 2 +- src/serial_win32.c | 2 +- src/usbhid.c | 2 +- 7 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/bluetooth.c b/src/bluetooth.c index e6fafd5..1889bf7 100644 --- a/src/bluetooth.c +++ b/src/bluetooth.c @@ -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 (!dc_descriptor_filter (iterator->descriptor, 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 06c2980..eaf7303 100644 --- a/src/descriptor-private.h +++ b/src/descriptor-private.h @@ -34,7 +34,7 @@ typedef struct dc_usb_desc_t { } dc_usb_desc_t; int -dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata); +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 183d3d5..5acab3f 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -36,21 +36,31 @@ 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 *); -typedef int (*dc_filter_t) (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); -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); +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 dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -463,13 +473,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; } } @@ -484,7 +497,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", @@ -519,7 +532,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 @@ -541,7 +554,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", @@ -557,7 +570,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", @@ -577,7 +590,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", @@ -592,7 +605,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", @@ -606,7 +619,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", @@ -619,7 +632,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 @@ -641,7 +654,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", @@ -748,10 +761,10 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor) } int -dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata) +dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params) { if (descriptor == NULL || descriptor->filter == NULL) return 1; - return descriptor->filter (transport, userdata); + return descriptor->filter (transport, userdata, params); } diff --git a/src/irda.c b/src/irda.c index 89b7e50..448c57c 100644 --- a/src/irda.c +++ b/src/irda.c @@ -231,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 (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name)) { + if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name, NULL)) { continue; } diff --git a/src/serial_posix.c b/src/serial_posix.c index cca501e..4fbcb96 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -230,7 +230,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out) return DC_STATUS_NOMEMORY; } - if (!dc_descriptor_filter (iterator->descriptor, 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 b51aafd..8427a25 100644 --- a/src/serial_win32.c +++ b/src/serial_win32.c @@ -226,7 +226,7 @@ dc_serial_iterator_next (dc_iterator_t *abstract, void *out) // Null terminate the string. data[data_len] = 0; - if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, data)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_SERIAL, data, NULL)) { continue; } diff --git a/src/usbhid.c b/src/usbhid.c index 9ed6648..b16bcc3 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -435,7 +435,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) } dc_usb_desc_t usb = {dev.idVendor, dev.idProduct}; - if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USBHID, &usb)) { + if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_USBHID, &usb, NULL)) { continue; } From c84bbd93a3ed898d45482d5c22ce0acee9701033 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 30 Apr 2020 19:10:34 +0200 Subject: [PATCH 3/5] Add an I/O implementation for USB communication The USB communication is now also implemented as an I/O stream transport. Unlike most I/O devices, USB communication supports multiple interfaces and endpoints. This requires some some special care: In the general case, autodetection isn't really possible without additional knowledge. Hence the need for the filter parameters to pass this kind of information. The implementation assumes two bulk endpoints for the standard read/write interface. Communication with the control endpoint is supported through the new DC_IOCTL_USB_CONTROL_{READ,WRITE} ioctl's. --- include/libdivecomputer/Makefile.am | 1 + include/libdivecomputer/usb.h | 145 +++++++ msvc/libdivecomputer.vcproj | 8 + src/Makefile.am | 1 + src/descriptor-private.h | 6 + src/libdivecomputer.symbols | 6 + src/usb.c | 585 ++++++++++++++++++++++++++++ 7 files changed, 752 insertions(+) create mode 100644 include/libdivecomputer/usb.h create mode 100644 src/usb.c 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 From c72dc4aa738915292917a538f02a61b928233383 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 30 Apr 2020 19:17:23 +0200 Subject: [PATCH 4/5] Use the new USB transport for the Atomic Aquatics Cobalt Replace the hardcoded libusb based code with the new USB I/O transport. This enables the use of a custom I/O on platforms where libusb is not available. --- src/atomics_cobalt.c | 152 +++++++++++++------------------------------ src/atomics_cobalt.h | 3 +- src/descriptor.c | 22 ++++++- src/device.c | 2 +- 4 files changed, 70 insertions(+), 109 deletions(-) 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/descriptor.c b/src/descriptor.c index 5acab3f..cc8bb9a 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -61,6 +61,7 @@ static int dc_filter_mares (dc_transport_t transport, const void *userdata, void 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); @@ -326,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 */ @@ -669,6 +670,23 @@ static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void 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) { 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); From 5380b247af47dea198aaf2c68ac7aaf0cc8ef69e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 30 Apr 2020 19:23:59 +0200 Subject: [PATCH 5/5] Update the example application The application is now responsible for setting up the USB based I/O stream. --- examples/common.c | 38 +++++++++++++++++++++++++++++++++++++- examples/dctool_scan.c | 8 ++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) 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);