Merge git://github.com/libdivecomputer/libdivecomputer into Subsurface-DS9

Merge upstream changes by Jef Driesen:

 - add support for Liquivision dive computers

 - add support for the Aqualung i470TC

 - extract out Atomic Aquatics Cobalt USB support as a iostream

 - misc fixes

* git://github.com/libdivecomputer/libdivecomputer:
  Fix the OSTC4 firmware upgrade
  Handle a negative number of bytes as an error
  Update the example application
  Use the new USB transport for the Atomic Aquatics Cobalt
  Add an I/O implementation for USB communication
  Add support for filter parameters
  Disable direct access to the filter function
  Increase the receive timeout to 5 seconds
  Fix the McLean Extreme bluetooth name
  Add support for Liquivision dive computers
  Add support for the Aqualung i470TC
This commit is contained in:
Linus Torvalds 2020-08-20 12:36:38 -07:00
commit 3c1e763a47
29 changed files with 2443 additions and 252 deletions

View File

@ -31,6 +31,7 @@
#include <libdivecomputer/serial.h>
#include <libdivecomputer/bluetooth.h>
#include <libdivecomputer/irda.h>
#include <libdivecomputer/usb.h>
#include <libdivecomputer/usbhid.h>
#include "common.h"
@ -90,9 +91,12 @@ static const backend_table_t g_backends[] = {
{"idive", DC_FAMILY_DIVESYSTEM_IDIVE, 0x03},
{"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0},
{"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0},
{"mclean", DC_FAMILY_MCLEAN_EXTREME, 0},
{"lynx", DC_FAMILY_LIQUIVISION_LYNX, 0},
// Not merged upstream yet
{"descentmk1", DC_FAMILY_GARMIN, 0},
{"cosmiq", DC_FAMILY_DEEPBLU, 0},
{"mclean", DC_FAMILY_MCLEAN_EXTREME, 0},
{"oceans", DC_FAMILY_OCEANS_S1, 0},
};
@ -103,6 +107,8 @@ static const transport_table_t g_transports[] = {
{"irda", DC_TRANSPORT_IRDA},
{"bluetooth", DC_TRANSPORT_BLUETOOTH},
{"ble", DC_TRANSPORT_BLE},
// Not merged upstream yet
{"usbstorage",DC_TRANSPORT_USBSTORAGE},
};
@ -401,6 +407,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)
{
@ -535,16 +576,18 @@ 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:
return dctool_irda_open (iostream, context, descriptor, devname);
case DC_TRANSPORT_BLUETOOTH:
return dctool_bluetooth_open (iostream, context, descriptor, devname);
case DC_TRANSPORT_USBSTORAGE:
return dc_usb_storage_open (iostream, context, devname);
default:
return DC_STATUS_UNSUPPORTED;
// Not merged upstream yet
case DC_TRANSPORT_USBSTORAGE:
return dc_usb_storage_open (iostream, context, devname);
}
}

View File

@ -36,6 +36,7 @@
#include <libdivecomputer/serial.h>
#include <libdivecomputer/irda.h>
#include <libdivecomputer/bluetooth.h>
#include <libdivecomputer/usb.h>
#include <libdivecomputer/usbhid.h>
#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);

View File

@ -12,6 +12,7 @@ libdivecomputer_HEADERS = \
bluetooth.h \
ble.h \
irda.h \
usb.h \
usbhid.h \
custom.h \
device.h \

View File

@ -110,12 +110,16 @@ typedef enum dc_family_t {
DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16),
/* McLean */
DC_FAMILY_MCLEAN_EXTREME = (16 << 16),
/* Liquivision */
DC_FAMILY_LIQUIVISION_LYNX = (17 << 16),
// Not merged upstream yet
/* Garmin */
DC_FAMILY_GARMIN = (17 << 16),
DC_FAMILY_GARMIN = (100 << 16),
/* Deepblu */
DC_FAMILY_DEEPBLU = (18 << 16),
DC_FAMILY_DEEPBLU = (101 << 16),
/* Oceans S1 */
DC_FAMILY_OCEANS_S1 = (19 << 16),
DC_FAMILY_OCEANS_S1 = (102 << 16),
} dc_family_t;
#ifdef __cplusplus

View File

@ -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 */

View File

@ -318,6 +318,14 @@
RelativePath="..\src\iterator.c"
>
</File>
<File
RelativePath="..\src\liquivision_lynx.c"
>
</File>
<File
RelativePath="..\src\liquivision_lynx_parser.c"
>
</File>
<File
RelativePath="..\src\mares_common.c"
>
@ -546,6 +554,10 @@
RelativePath="..\src\timer.c"
>
</File>
<File
RelativePath="..\src\usb.c"
>
</File>
<File
RelativePath="..\src\usbhid.c"
>
@ -720,6 +732,10 @@
RelativePath="..\include\libdivecomputer\iterator.h"
>
</File>
<File
RelativePath="..\src\liquivision_lynx.h"
>
</File>
<File
RelativePath="..\src\mares_common.h"
>
@ -912,6 +928,10 @@
RelativePath="..\include\libdivecomputer\units.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\usb.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\usbhid.h"
>

View File

@ -69,21 +69,26 @@ libdivecomputer_la_SOURCES = \
rbstream.h rbstream.c \
checksum.h checksum.c \
array.h array.c \
field-cache.h field-cache.c \
buffer.c \
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
garmin.h garmin.c garmin_parser.c \
deepblu.h deepblu.c deepblu_parser.c \
mclean_extreme.h mclean_extreme.c mclean_extreme_parser.c \
oceans_s1.h oceans_s1.c oceans_s1_parser.c \
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
socket.h socket.c \
irda.c \
usb.c \
usbhid.c \
bluetooth.c \
usb_storage.c \
custom.c
# Not merged upstream yet
libdivecomputer_la_SOURCES += \
usb_storage.c \
field-cache.h field-cache.c \
garmin.h garmin.c garmin_parser.c \
deepblu.h deepblu.c deepblu_parser.c \
oceans_s1.h oceans_s1.c oceans_s1_parser.c
if OS_WIN32
libdivecomputer_la_SOURCES += serial_win32.c
else

View File

@ -19,19 +19,10 @@
* MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h> // memcmp, memcpy
#include <stdlib.h> // malloc, free
#ifdef HAVE_LIBUSB
#ifdef _WIN32
#define NOGDI
#endif
#include <libusb.h>
#endif
#include <libdivecomputer/usb.h>
#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
}

View File

@ -23,6 +23,7 @@
#define ATOMICS_COBALT_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#include <libdivecomputer/atomics_cobalt.h>
@ -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);

View File

@ -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;
}

View File

@ -157,3 +157,48 @@ checksum_crc32 (const unsigned char data[], unsigned int size)
return crc ^ 0xffffffff;
}
unsigned int
checksum_crc32b (const unsigned char data[], unsigned int size)
{
static const unsigned int crc_table[] = {
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,
0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,
0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,
0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,
0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,
0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,
0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,
0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,
0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4,
};
unsigned int crc = 0xffffffff;
for (unsigned int i = 0; i < size; ++i)
crc = crc_table[((crc >> 24) ^ data[i]) & 0xFF] ^ (crc << 8);
return crc ^ 0xffffffff;
}

View File

@ -44,6 +44,9 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh
unsigned int
checksum_crc32 (const unsigned char data[], unsigned int size);
unsigned int
checksum_crc32b (const unsigned char data[], unsigned int size);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -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
}

View File

@ -36,22 +36,37 @@
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_garmin (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_deepblu (dc_transport_t transport, const void *userdata);
static int dc_filter_mclean (dc_transport_t transport, const void *userdata);
static int dc_filter_oceans(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);
// Not merged upstream yet
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_oceans(dc_transport_t transport, const void *userdata, void *params);
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
@ -251,6 +266,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Oceanic", "Veo 4.0", DC_FAMILY_OCEANIC_ATOM2, 0x4654, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Sherwood", "Wisdom 4", DC_FAMILY_OCEANIC_ATOM2, 0x4655, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
/* Mares Nemo */
{"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
@ -316,8 +332,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 */
@ -380,12 +396,19 @@ static const dc_descriptor_t g_descriptors[] = {
{"Cochran", "EMC-20H", DC_FAMILY_COCHRAN_COMMANDER, 5, DC_TRANSPORT_SERIAL, NULL},
/* Tecdiving DiveComputer.eu */
{"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving},
/* McLean Extreme */
{ "McLean", "Extreme", DC_FAMILY_MCLEAN_EXTREME, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_mclean},
/* Liquivision */
{"Liquivision", "Xen", DC_FAMILY_LIQUIVISION_LYNX, 0, DC_TRANSPORT_SERIAL, NULL},
{"Liquivision", "Xeo", DC_FAMILY_LIQUIVISION_LYNX, 1, DC_TRANSPORT_SERIAL, NULL},
{"Liquivision", "Lynx", DC_FAMILY_LIQUIVISION_LYNX, 2, DC_TRANSPORT_SERIAL, NULL},
{"Liquivision", "Kaon", DC_FAMILY_LIQUIVISION_LYNX, 3, DC_TRANSPORT_SERIAL, NULL},
// Not merged upstream yet
/* Garmin */
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin},
/* Deepblu */
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
/* McLean Extreme */
{ "McLean", "Extreme", DC_FAMILY_MCLEAN_EXTREME, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_mclean},
/* Oceans S1 */
{ "Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans },
};
@ -464,13 +487,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;
}
}
@ -485,7 +511,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",
@ -520,7 +546,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
@ -542,7 +568,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",
@ -558,7 +584,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",
@ -578,7 +604,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",
@ -593,20 +619,7 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
return 1;
}
static int dc_filter_garmin (dc_transport_t transport, const void *userdata)
{
static const dc_usb_desc_t usbhid[] = {
{0x091e, 0x2b2b}, // Garmin Descent Mk1
};
if (transport == DC_TRANSPORT_USBSTORAGE) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb);
}
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",
@ -620,7 +633,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",
@ -633,7 +646,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
@ -645,6 +658,7 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata)
0x4654, // Oceanic Veo 4.0
0x4655, // Sherwood Wisdom 4
0x4656, // Oceanic Pro Plus 4
0x4743, // Aqualung i470TC
};
if (transport == DC_TRANSPORT_BLE) {
@ -654,7 +668,53 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata)
return 1;
}
static int dc_filter_deepblu (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",
};
if (transport == DC_TRANSPORT_BLUETOOTH) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
} else if (transport == DC_TRANSPORT_SERIAL) {
return DC_FILTER_INTERNAL(userdata, rfcomm, 1, dc_match_devname);
}
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;
}
// Not merged upstream yet
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params)
{
static const dc_usb_desc_t usbhid[] = {
{0x091e, 0x2b2b}, // Garmin Descent Mk1
};
if (transport == DC_TRANSPORT_USBSTORAGE) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb);
}
return 1;
}
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params)
{
static const char * const bluetooth[] = {
"COSMIQ",
@ -667,22 +727,7 @@ static int dc_filter_deepblu (dc_transport_t transport, const void *userdata)
return 1;
}
static int dc_filter_mclean(dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"Extreme",
};
if (transport == DC_TRANSPORT_BLUETOOTH) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
} else if (transport == DC_TRANSPORT_SERIAL) {
return DC_FILTER_INTERNAL(userdata, rfcomm, 1, dc_match_devname);
}
return 1;
}
static int dc_filter_oceans(dc_transport_t transport, const void* userdata)
static int dc_filter_oceans(dc_transport_t transport, const void* userdata, void *params)
{
static const char* const ble[] = {
"S1",
@ -786,11 +831,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);
}

View File

@ -57,9 +57,12 @@
#include "divesystem_idive.h"
#include "cochran_commander.h"
#include "tecdiving_divecomputereu.h"
#include "mclean_extreme.h"
#include "liquivision_lynx.h"
// Not merged upstream yet
#include "garmin.h"
#include "deepblu.h"
#include "mclean_extreme.h"
#include "oceans_s1.h"
#include "device-private.h"
@ -192,7 +195,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);
@ -215,20 +218,25 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_device_open (&device, context, iostream);
break;
case DC_FAMILY_MCLEAN_EXTREME:
rc = mclean_extreme_device_open (&device, context, iostream);
break;
case DC_FAMILY_LIQUIVISION_LYNX:
rc = liquivision_lynx_device_open (&device, context, iostream);
break;
default:
return DC_STATUS_INVALIDARGS;
// Not merged upstream yet
case DC_FAMILY_GARMIN:
rc = garmin_device_open (&device, context, iostream);
break;
case DC_FAMILY_DEEPBLU:
rc = deepblu_device_open (&device, context, iostream);
break;
case DC_FAMILY_MCLEAN_EXTREME:
rc = mclean_extreme_device_open (&device, context, iostream);
break;
case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_device_open(&device, context, iostream);
break;
default:
return DC_STATUS_INVALIDARGS;
}
*out = device;

View File

@ -468,39 +468,25 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device)
const unsigned char command[] = {S_INIT, 0xAB, 0xCD, 0xEF};
unsigned char answer[5] = {0};
for (size_t i = 0; i < 4; ++i) {
// Send the command.
status = hw_ostc3_write (device, NULL, command + i, 1);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
// Read the answer.
status = hw_ostc3_read (device, NULL, answer + i, 1);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
// Verify the answer.
const unsigned char expected = (i == 0 ? 0x4B : command[i]);
if (answer[i] != expected) {
ERROR (abstract->context, "Unexpected answer byte.");
return DC_STATUS_PROTOCOL;
}
}
// Read the ready byte.
status = hw_ostc3_read (device, NULL, answer + 4, 1);
// Send the command and service key.
status = hw_ostc3_write (device, NULL, command, sizeof (command));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the ready byte.");
ERROR (abstract->context, "Failed to send the command.");
return status;
}
// Verify the ready byte.
if (answer[4] != S_READY) {
ERROR (abstract->context, "Unexpected ready byte.");
// Read the response.
status = hw_ostc3_read (device, NULL, answer, sizeof (answer));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
// Verify the response to service mode.
if (answer[0] != 0x4B || answer[1] != 0xAB ||
answer[2] != 0xCD || answer[3] != 0xEF ||
answer[4] != S_READY) {
ERROR (abstract->context, "Failed to verify the answer.");
return DC_STATUS_PROTOCOL;
}

View File

@ -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;
}

View File

@ -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

677
src/liquivision_lynx.c Normal file
View File

@ -0,0 +1,677 @@
/*
* 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
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libdivecomputer/units.h>
#include "liquivision_lynx.h"
#include "context-private.h"
#include "device-private.h"
#include "ringbuffer.h"
#include "rbstream.h"
#include "checksum.h"
#include "array.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &liquivision_lynx_device_vtable)
#define XEN 0
#define XEO 1
#define LYNX 2
#define KAON 3
#define XEN_V1 0x83321485 // Not supported
#define XEN_V2 0x83321502
#define XEN_V3 0x83328401
#define XEO_V1_A 0x17485623
#define XEO_V1_B 0x27485623
#define XEO_V2_A 0x17488401
#define XEO_V2_B 0x27488401
#define XEO_V3_A 0x17488402
#define XEO_V3_B 0x27488402
#define LYNX_V1 0x67488403
#define LYNX_V2 0x67488404
#define LYNX_V3 0x67488405
#define KAON_V1 0x37488402
#define KAON_V2 0x47488402
#define MAXRETRIES 2
#define MAXPACKET 12
#define SEGMENTSIZE 0x400
#define PAGESIZE 0x1000
#define MEMSIZE 0x200000
#define RB_LOGBOOK_BEGIN (1 * PAGESIZE)
#define RB_LOGBOOK_END (25 * PAGESIZE)
#define RB_LOGBOOK_SIZE (RB_LOGBOOK_END - RB_LOGBOOK_BEGIN)
#define RB_LOGBOOK_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END)
#define RB_PROFILE_BEGIN (25 * PAGESIZE)
#define RB_PROFILE_END (500 * PAGESIZE)
#define RB_PROFILE_SIZE (RB_PROFILE_END - RB_PROFILE_BEGIN)
#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_PROFILE_BEGIN, RB_PROFILE_END)
#define SZ_HEADER_XEN 80
#define SZ_HEADER_OTHER 96
#define SZ_HEADER_MAX SZ_HEADER_OTHER
typedef struct liquivision_lynx_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[4];
} liquivision_lynx_device_t;
static dc_status_t liquivision_lynx_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t liquivision_lynx_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static dc_status_t liquivision_lynx_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
static dc_status_t liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t liquivision_lynx_device_close (dc_device_t *abstract);
static const dc_device_vtable_t liquivision_lynx_device_vtable = {
sizeof(liquivision_lynx_device_t),
DC_FAMILY_LIQUIVISION_LYNX,
liquivision_lynx_device_set_fingerprint, /* set_fingerprint */
liquivision_lynx_device_read, /* read */
NULL, /* write */
liquivision_lynx_device_dump, /* dump */
liquivision_lynx_device_foreach, /* foreach */
NULL, /* timesync */
liquivision_lynx_device_close /* close */
};
static dc_status_t
liquivision_lynx_send (liquivision_lynx_device_t *device, const unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (size > MAXPACKET)
return DC_STATUS_INVALIDARGS;
// Build the packet.
unsigned char packet[2 + MAXPACKET + 2] = {0};
packet[0] = 0x00;
packet[1] = 0xB1;
if (size) {
memcpy (packet + 2, data, size);
}
packet[2 + size + 0] = 0x0B;
packet[2 + size + 1] = 0x0E;
// Send the packet to the device.
status = dc_iostream_write (device->iostream, packet, size + 4, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the packet.");
return status;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_recv (liquivision_lynx_device_t *device, unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (size > SEGMENTSIZE)
return DC_STATUS_INVALIDARGS;
// Receive the packet from the device.
unsigned char packet[1 + SEGMENTSIZE + 2] = {0};
status = dc_iostream_read (device->iostream, packet, 1 + size + 2, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet.");
return status;
}
// Verify the start byte.
if (packet[0] != 0xC5) {
ERROR (abstract->context, "Unexpected answer start byte (%02x).", packet[0]);
return DC_STATUS_PROTOCOL;
}
// Verify the checksum.
unsigned short crc = array_uint16_be (packet + 1 + size);
unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
}
if (size) {
memcpy (data, packet + 1, size);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_packet (liquivision_lynx_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
status = liquivision_lynx_send (device, command, csize);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
if (asize) {
status = liquivision_lynx_recv (device, answer, asize);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_transfer (liquivision_lynx_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
{
unsigned int nretries = 0;
dc_status_t rc = DC_STATUS_SUCCESS;
while ((rc = liquivision_lynx_packet (device, command, csize, answer, asize)) != DC_STATUS_SUCCESS) {
if (rc != DC_STATUS_TIMEOUT && rc != DC_STATUS_PROTOCOL)
return rc;
// Abort if the maximum number of retries is reached.
if (nretries++ >= MAXRETRIES)
return rc;
// Delay the next attempt.
dc_iostream_sleep (device->iostream, 100);
dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
}
return DC_STATUS_SUCCESS;
}
dc_status_t
liquivision_lynx_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
liquivision_lynx_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (liquivision_lynx_device_t *) dc_device_allocate (context, &liquivision_lynx_device_vtable);
if (device == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
device->iostream = iostream;
memset (device->fingerprint, 0, sizeof (device->fingerprint));
// Set the serial communication protocol (9600 8N1).
status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
goto error_free;
}
// Set the timeout for receiving data (3000 ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
}
// Set the DTR line.
status = dc_iostream_set_dtr (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the DTR line.");
goto error_free;
}
// Set the RTS line.
status = dc_iostream_set_rts (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the RTS line.");
goto error_free;
}
// Make sure everything is in a sane state.
dc_iostream_sleep (device->iostream, 100);
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
// Wakeup the device.
for (unsigned int i = 0; i < 6000; ++i) {
const unsigned char init[] = {0xAA};
dc_iostream_write (device->iostream, init, sizeof (init), NULL);
}
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
static dc_status_t
liquivision_lynx_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
liquivision_lynx_device_t *device = (liquivision_lynx_device_t *) abstract;
if (size && size != sizeof (device->fingerprint))
return DC_STATUS_INVALIDARGS;
if (size)
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
else
memset (device->fingerprint, 0, sizeof (device->fingerprint));
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
liquivision_lynx_device_t *device = (liquivision_lynx_device_t *) abstract;
if ((address % SEGMENTSIZE != 0) ||
(size % SEGMENTSIZE != 0))
return DC_STATUS_INVALIDARGS;
// Get the page and segment number.
unsigned int page = (address / PAGESIZE);
unsigned int segment = (address % PAGESIZE) / SEGMENTSIZE;
unsigned int nbytes = 0;
while (nbytes < size) {
const unsigned char command[] = {
0x50, 0x41, 0x47, 0x45,
'0' + ((page / 100) % 10),
'0' + ((page / 10) % 10),
'0' + ((page / 1) % 10),
'0' + ((page / 100) % 10),
'0' + ((page / 10) % 10),
'0' + ((page / 1) % 10),
'0' + segment,
'0' + segment
};
status = liquivision_lynx_transfer (device, command, sizeof(command), data + nbytes, SEGMENTSIZE);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read page %u segment %u.", page, segment);
return status;
}
nbytes += SEGMENTSIZE;
segment++;
if (segment == (PAGESIZE / SEGMENTSIZE)) {
segment = 0;
page++;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
{
// Allocate the required amount of memory.
if (!dc_buffer_resize (buffer, MEMSIZE)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
return device_dump_read (abstract, dc_buffer_get_data (buffer),
dc_buffer_get_size (buffer), SEGMENTSIZE);
}
static dc_status_t
liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
liquivision_lynx_device_t *device = (liquivision_lynx_device_t *) abstract;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
progress.maximum = SEGMENTSIZE + RB_LOGBOOK_SIZE + RB_PROFILE_SIZE;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Send the info command.
unsigned char rsp_info[6] = {0};
const unsigned char cmd_info[] = {0x49, 0x4E, 0x46, 0x4F, 0x49, 0x4E, 0x46, 0x4F, 0x49, 0x4E, 0x46, 0x4F};
status = liquivision_lynx_transfer (device, cmd_info, sizeof(cmd_info), rsp_info, sizeof(rsp_info));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the info command.");
goto error_exit;
}
// Get the model and version.
unsigned int model = array_uint16_le(rsp_info + 0);
unsigned int version = array_uint32_le(rsp_info + 2);
// Send the more info command.
unsigned char rsp_more[12] = {0};
const unsigned char cmd_more[] = {0x4D, 0x4F, 0x52, 0x45, 0x49, 0x4E, 0x46, 0x4F, 0x4D, 0x4F, 0x52, 0x45};
status = liquivision_lynx_transfer (device, cmd_more, sizeof(cmd_more), rsp_more, sizeof(rsp_more));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the more info command.");
goto error_exit;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = model;
devinfo.firmware = 0;
devinfo.serial = array_uint32_le(rsp_more + 0);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Read the config segment.
unsigned char config[SEGMENTSIZE] = {0};
status = liquivision_lynx_device_read (abstract, 0, config, sizeof (config));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
goto error_exit;
}
// Get the header size.
unsigned int headersize = (model == XEN) ? SZ_HEADER_XEN : SZ_HEADER_OTHER;
// Get the number of headers per page.
unsigned int npages = PAGESIZE / headersize;
// Get the logbook pointers.
unsigned int begin = array_uint16_le (config + 0x46);
unsigned int end = array_uint16_le (config + 0x48);
unsigned int rb_logbook_begin = RB_LOGBOOK_BEGIN + (begin / npages) * PAGESIZE + (begin % npages) * headersize;
unsigned int rb_logbook_end = RB_LOGBOOK_BEGIN + (end / npages) * PAGESIZE + (end % npages) * headersize;
if (rb_logbook_begin < RB_LOGBOOK_BEGIN || rb_logbook_begin > RB_LOGBOOK_END ||
rb_logbook_end < RB_LOGBOOK_BEGIN || rb_logbook_end > RB_LOGBOOK_END) {
ERROR (abstract->context, "Invalid logbook pointers (%04x, %04x).",
rb_logbook_begin, rb_logbook_end);
status = DC_STATUS_DATAFORMAT;
goto error_exit;
}
// Calculate the logbook size.
#if 0
unsigned int rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_begin, rb_logbook_end);
#else
// The logbook begin pointer is explicitly ignored, because it only takes
// into account dives for which the profile is still available.
unsigned int rb_logbook_size = RB_LOGBOOK_SIZE;
#endif
// Get the profile pointers.
unsigned int rb_profile_begin = array_uint32_le (config + 0x4A);
unsigned int rb_profile_end = array_uint32_le (config + 0x4E);
if (rb_profile_begin < RB_PROFILE_BEGIN || rb_profile_begin > RB_PROFILE_END ||
rb_profile_end < RB_PROFILE_BEGIN || rb_profile_end > RB_PROFILE_END) {
ERROR (abstract->context, "Invalid profile pointers (%04x, %04x).",
rb_profile_begin, rb_profile_end);
status = DC_STATUS_DATAFORMAT;
goto error_exit;
}
// Update and emit a progress event.
progress.current += SEGMENTSIZE;
progress.maximum -= RB_LOGBOOK_SIZE - rb_logbook_size;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Allocate memory for the logbook entries.
unsigned char *logbook = (unsigned char *) malloc (rb_logbook_size);
if (logbook == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
// Create the ringbuffer stream.
dc_rbstream_t *rblogbook = NULL;
status = dc_rbstream_new (&rblogbook, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END, rb_logbook_end);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
goto error_free_logbook;
}
// The logbook ringbuffer is read backwards to retrieve the most recent
// entries first. If an already downloaded entry is identified (by means
// of its fingerprint), the transfer is aborted immediately to reduce
// the transfer time.
unsigned int nbytes = 0;
unsigned int offset = rb_logbook_size;
unsigned int address = rb_logbook_end;
while (nbytes < rb_logbook_size) {
// Handle the ringbuffer wrap point.
if (address == RB_LOGBOOK_BEGIN)
address = RB_LOGBOOK_END;
// Skip the padding bytes.
if ((address % PAGESIZE) == 0) {
unsigned int padding = PAGESIZE % headersize;
unsigned char dummy[SZ_HEADER_MAX] = {0};
status = dc_rbstream_read (rblogbook, &progress, dummy, padding);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
goto error_free_rblogbook;
}
address -= padding;
nbytes += padding;
}
// Move to the start of the current entry.
address -= headersize;
offset -= headersize;
// Read the logbook entry.
status = dc_rbstream_read (rblogbook, &progress, logbook + offset, headersize);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
goto error_free_rblogbook;
}
nbytes += headersize;
if (array_isequal (logbook + offset, headersize, 0xFF)) {
offset += headersize;
break;
}
// Verify the checksum.
unsigned int unused = 2;
if (version == XEO_V1_A || version == XEO_V1_B) {
unused = 6;
}
unsigned char header[SZ_HEADER_MAX] = {0};
memcpy (header + 0, rsp_info + 2, 4);
memcpy (header + 4, logbook + offset + 4, headersize - 4);
unsigned int crc = array_uint32_le (logbook + offset + 0);
unsigned int ccrc = checksum_crc32b (header, headersize - unused);
if (crc != ccrc) {
WARNING (abstract->context, "Invalid dive checksum (%08x %08x)", crc, ccrc);
status = DC_STATUS_DATAFORMAT;
goto error_free_rblogbook;
}
// Compare the fingerprint to identify previously downloaded entries.
if (memcmp (logbook + offset, device->fingerprint, sizeof(device->fingerprint)) == 0) {
offset += headersize;
break;
}
}
// Update and emit a progress event.
progress.maximum -= rb_logbook_size - nbytes;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Go through the logbook entries a first time, to calculate the total
// amount of bytes in the profile ringbuffer.
unsigned int rb_profile_size = 0;
// Traverse the logbook ringbuffer backwards to retrieve the most recent
// dives first. The logbook ringbuffer is linearized at this point, so
// we do not have to take into account any memory wrapping near the end
// of the memory buffer.
unsigned int remaining = RB_PROFILE_SIZE;
unsigned int previous = rb_profile_end;
unsigned int entry = rb_logbook_size;
while (entry != offset) {
// Move to the start of the current entry.
entry -= headersize;
// Get the profile pointer.
unsigned int current = array_uint32_le (logbook + entry + 16);
if (current < RB_PROFILE_BEGIN || current >= RB_PROFILE_END) {
ERROR (abstract->context, "Invalid profile ringbuffer pointer (%08x).", current);
status = DC_STATUS_DATAFORMAT;
goto error_free_rblogbook;
}
// Calculate the length.
unsigned int length = RB_PROFILE_DISTANCE (current, previous);
// Make sure the profile size is valid.
if (length > remaining) {
remaining = 0;
length = 0;
}
// Update the total profile size.
rb_profile_size += length;
// Move to the start of the current dive.
remaining -= length;
previous = current;
}
// At this point, we know the exact amount of data
// that needs to be transferred for the profiles.
progress.maximum -= RB_PROFILE_SIZE - rb_profile_size;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Allocate memory for the profile data.
unsigned char *profile = (unsigned char *) malloc (headersize + rb_profile_size);
if (profile == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_free_rblogbook;
}
// Create the ringbuffer stream.
dc_rbstream_t *rbprofile = NULL;
status = dc_rbstream_new (&rbprofile, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_PROFILE_BEGIN, RB_PROFILE_END, rb_profile_end);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
goto error_free_profile;
}
// Traverse the logbook ringbuffer backwards to retrieve the most recent
// dives first. The logbook ringbuffer is linearized at this point, so
// we do not have to take into account any memory wrapping near the end
// of the memory buffer.
remaining = rb_profile_size;
previous = rb_profile_end;
entry = rb_logbook_size;
while (entry != offset) {
// Move to the start of the current entry.
entry -= headersize;
// Get the profile pointer.
unsigned int current = array_uint32_le (logbook + entry + 16);
if (current < RB_PROFILE_BEGIN || current >= RB_PROFILE_END) {
ERROR (abstract->context, "Invalid profile ringbuffer pointer (%08x).", current);
status = DC_STATUS_DATAFORMAT;
goto error_free_rbprofile;
}
// Calculate the length.
unsigned int length = RB_PROFILE_DISTANCE (current, previous);
// Make sure the profile size is valid.
if (length > remaining) {
remaining = 0;
length = 0;
}
// Move to the start of the current dive.
remaining -= length;
previous = current;
// Read the dive.
status = dc_rbstream_read (rbprofile, &progress, profile + remaining + headersize, length);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive.");
goto error_free_rbprofile;
}
// Prepend the logbook entry to the profile data. The memory buffer is
// large enough to store this entry. The checksum is replaced with the
// flash version number.
memcpy (profile + remaining + 0, rsp_info + 2, 4);
memcpy (profile + remaining + 4, logbook + entry + 4, headersize - 4);
if (callback && !callback (profile + remaining, headersize + length, logbook + entry, sizeof(device->fingerprint), userdata)) {
break;
}
}
error_free_rbprofile:
dc_rbstream_free (rbprofile);
error_free_profile:
free (profile);
error_free_rblogbook:
dc_rbstream_free (rblogbook);
error_free_logbook:
free (logbook);
error_exit:
return status;
}
static dc_status_t
liquivision_lynx_device_close (dc_device_t *abstract)
{
dc_status_t status = DC_STATUS_SUCCESS;
liquivision_lynx_device_t *device = (liquivision_lynx_device_t*) abstract;
dc_status_t rc = DC_STATUS_SUCCESS;
// Send the finish command.
const unsigned char cmd_finish[] = {0x46, 0x49, 0x4E, 0x49, 0x53, 0x48, 0x46, 0x49, 0x4E, 0x49, 0x53, 0x48};
status = liquivision_lynx_transfer (device, cmd_finish, sizeof(cmd_finish), NULL, 0);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the finish command.");
dc_status_set_error(&status, rc);
}
return status;
}

43
src/liquivision_lynx.h Normal file
View File

@ -0,0 +1,43 @@
/*
* 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 LIQUIVISION_LYNX_H
#define LIQUIVISION_LYNX_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
dc_status_t
liquivision_lynx_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
liquivision_lynx_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LIQUIVISION_LYNX_H */

View File

@ -0,0 +1,596 @@
/*
* 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
*/
#include <stdlib.h>
#include <string.h>
#include <libdivecomputer/units.h>
#include "liquivision_lynx.h"
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#define ISINSTANCE(parser) dc_parser_isinstance((parser), &liquivision_lynx_parser_vtable)
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define XEN 0
#define XEO 1
#define LYNX 2
#define KAON 3
#define XEN_V1 0x83321485 // Not supported
#define XEN_V2 0x83321502
#define XEN_V3 0x83328401
#define XEO_V1_A 0x17485623
#define XEO_V1_B 0x27485623
#define XEO_V2_A 0x17488401
#define XEO_V2_B 0x27488401
#define XEO_V3_A 0x17488402
#define XEO_V3_B 0x27488402
#define LYNX_V1 0x67488403
#define LYNX_V2 0x67488404
#define LYNX_V3 0x67488405
#define KAON_V1 0x37488402
#define KAON_V2 0x47488402
#define SZ_HEADER_XEN 80
#define SZ_HEADER_OTHER 96
#define FRESH 0
#define BRACKISH 1
#define SALT 2
#define DECO 0
#define GAUGE 1
#define TEC 2
#define REC 3
#define NORMAL 0
#define BOOKMARK 1
#define ALARM_DEPTH 2
#define ALARM_TIME 3
#define ALARM_VELOCITY 4
#define DECOSTOP 5
#define DECOSTOP_BREACHED 6
#define GASMIX 7
#define SETPOINT 8
#define BAILOUT_ON 9
#define BAILOUT_OFF 10
#define EMERGENCY_ON 11
#define EMERGENCY_OFF 12
#define LOST_GAS 13
#define SAFETY_STOP 14
#define TANK_PRESSURE 15
#define TANK_LIST 16
#define NGASMIXES 11
#define NTANKS 11
#define INVALID 0xFFFFFFFF
typedef struct liquivision_lynx_parser_t liquivision_lynx_parser_t;
typedef struct liquivision_lynx_gasmix_t {
unsigned int oxygen;
unsigned int helium;
} liquivision_lynx_gasmix_t;
typedef struct liquivision_lynx_tank_t {
unsigned int id;
unsigned int beginpressure;
unsigned int endpressure;
} liquivision_lynx_tank_t;
struct liquivision_lynx_parser_t {
dc_parser_t base;
unsigned int model;
unsigned int headersize;
// Cached fields.
unsigned int cached;
unsigned int ngasmixes;
unsigned int ntanks;
liquivision_lynx_gasmix_t gasmix[NGASMIXES];
liquivision_lynx_tank_t tank[NTANKS];
};
static dc_status_t liquivision_lynx_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t liquivision_lynx_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t liquivision_lynx_parser_vtable = {
sizeof(liquivision_lynx_parser_t),
DC_FAMILY_LIQUIVISION_LYNX,
liquivision_lynx_parser_set_data, /* set_data */
liquivision_lynx_parser_get_datetime, /* datetime */
liquivision_lynx_parser_get_field, /* fields */
liquivision_lynx_parser_samples_foreach, /* samples_foreach */
NULL /* destroy */
};
dc_status_t
liquivision_lynx_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
{
liquivision_lynx_parser_t *parser = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (liquivision_lynx_parser_t *) dc_parser_allocate (context, &liquivision_lynx_parser_vtable);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
parser->model = model;
parser->headersize = (model == XEN) ? SZ_HEADER_XEN : SZ_HEADER_OTHER;
parser->cached = 0;
parser->ngasmixes = 0;
parser->ntanks = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
}
for (unsigned int i = 0; i < NTANKS; ++i) {
parser->tank[i].id = 0;
parser->tank[i].beginpressure = 0;
parser->tank[i].endpressure = 0;
}
*out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
liquivision_lynx_parser_t *parser = (liquivision_lynx_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->ngasmixes = 0;
parser->ntanks = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
}
for (unsigned int i = 0; i < NTANKS; ++i) {
parser->tank[i].id = 0;
parser->tank[i].beginpressure = 0;
parser->tank[i].endpressure = 0;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
liquivision_lynx_parser_t *parser = (liquivision_lynx_parser_t *) abstract;
if (abstract->size < parser->headersize)
return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data + 40;
if (datetime) {
datetime->year = array_uint16_le (p + 18);
datetime->month = array_uint16_le (p + 16) + 1;
datetime->day = array_uint16_le (p + 12) + 1;
datetime->hour = array_uint16_le (p + 8);
datetime->minute = array_uint16_le (p + 6);
datetime->second = array_uint16_le (p + 4);
datetime->timezone = DC_TIMEZONE_NONE;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
liquivision_lynx_parser_t *parser = (liquivision_lynx_parser_t *) abstract;
if (abstract->size < parser->headersize)
return DC_STATUS_DATAFORMAT;
if (!parser->cached) {
dc_status_t rc = liquivision_lynx_parser_samples_foreach (abstract, NULL, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_tank_t *tank = (dc_tank_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
*((unsigned int *) value) = array_uint32_le (abstract->data + 4);
break;
case DC_FIELD_MAXDEPTH:
*((double *) value) = array_uint16_le (abstract->data + 28) / 100.0;
break;
case DC_FIELD_AVGDEPTH:
*((double *) value) = array_uint16_le (abstract->data + 30) / 100.0;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
*((double *) value) = (signed short) array_uint16_le (abstract->data + 34) / 10.0;
break;
case DC_FIELD_TEMPERATURE_MAXIMUM:
*((double *) value) = (signed short) array_uint16_le (abstract->data + 36) / 10.0;
break;
case DC_FIELD_SALINITY:
switch (abstract->data[38]) {
case FRESH:
water->type = DC_WATER_FRESH;
water->density = 1000.0;
break;
case BRACKISH:
water->type = DC_WATER_SALT;
water->density = 1015.0;
break;
case SALT:
water->type = DC_WATER_SALT;
water->density = 1025.0;
break;
default:
return DC_STATUS_DATAFORMAT;
}
break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = array_uint16_le (abstract->data + 26) / 1000.0;
break;
case DC_FIELD_DIVEMODE:
if (parser->model == XEN) {
*((unsigned int *) value) = DC_DIVEMODE_GAUGE;
} else {
switch (abstract->data[92] & 0x0F) {
case DECO:
case TEC:
case REC:
*((unsigned int *) value) = DC_DIVEMODE_OC;
break;
case GAUGE:
*((unsigned int *) value) = DC_DIVEMODE_GAUGE;
break;
default:
return DC_STATUS_DATAFORMAT;
}
}
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_TANK_COUNT:
*((unsigned int *) value) = parser->ntanks;
break;
case DC_FIELD_TANK:
tank->type = DC_TANKVOLUME_NONE;
tank->volume = 0.0;
tank->workpressure = 0.0;
tank->beginpressure = parser->tank[flags].beginpressure / 100.0;
tank->endpressure = parser->tank[flags].endpressure / 100.0;
tank->gasmix = DC_GASMIX_UNKNOWN;
break;
default:
return DC_STATUS_UNSUPPORTED;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
liquivision_lynx_parser_t *parser = (liquivision_lynx_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (size < parser->headersize)
return DC_STATUS_DATAFORMAT;
// Get the version.
unsigned int version = array_uint32_le(data);
// Get the sample interval.
unsigned int interval_idx = data[39];
const unsigned int intervals[] = {1, 2, 5, 10, 30, 60};
if (interval_idx > C_ARRAY_SIZE(intervals)) {
ERROR (abstract->context, "Invalid sample interval index %u", interval_idx);
return DC_STATUS_DATAFORMAT;
}
unsigned int interval = intervals[interval_idx];
// Get the number of samples and events.
unsigned int nsamples = array_uint32_le (data + 8);
unsigned int nevents = array_uint32_le (data + 12);
unsigned int ngasmixes = 0;
unsigned int ntanks = 0;
liquivision_lynx_gasmix_t gasmix[NGASMIXES] = {0};
liquivision_lynx_tank_t tank[NTANKS] = {0};
unsigned int o2_previous = INVALID, he_previous = INVALID;
unsigned int gasmix_idx = INVALID;
unsigned int have_gasmix = 0;
unsigned int tank_id_previous = INVALID;
unsigned int tank_idx = INVALID;
unsigned int pressure[NTANKS] = {0};
unsigned int have_pressure = 0;
unsigned int setpoint = 0, have_setpoint = 0;
unsigned int deco = 0, have_deco = 0;
unsigned int time = 0;
unsigned int samples = 0;
unsigned int events = 0;
unsigned int offset = parser->headersize;
while (offset + 2 <= size) {
dc_sample_value_t sample = {0};
unsigned int value = array_uint16_le (data + offset);
offset += 2;
if (value & 0x8000) {
if (events >= nevents) {
break;
}
if (offset + 4 > size) {
ERROR (abstract->context, "Buffer overflow at offset %u", offset);
return DC_STATUS_DATAFORMAT;
}
unsigned int type = value & 0x7FFF;
unsigned int timestamp = array_uint32_le (data + offset + 2);
offset += 4;
// Get the sample length.
unsigned int length = 0;
switch (type) {
case DECOSTOP:
case GASMIX:
length = 2;
break;
case SETPOINT:
length = 1;
break;
case TANK_LIST:
length = NTANKS * 2;
break;
case TANK_PRESSURE:
if (version == LYNX_V1) {
length = 4;
} else {
length = 6;
}
break;
default:
break;
}
if (offset + length > size) {
ERROR (abstract->context, "Buffer overflow at offset %u", offset);
return DC_STATUS_DATAFORMAT;
}
unsigned int o2 = 0, he = 0;
unsigned int tank_id = 0, tank_pressure = 0;
switch (type) {
case NORMAL:
case BOOKMARK:
case ALARM_DEPTH:
case ALARM_TIME:
case ALARM_VELOCITY:
case DECOSTOP_BREACHED:
case BAILOUT_ON:
case BAILOUT_OFF:
case EMERGENCY_ON:
case EMERGENCY_OFF:
case LOST_GAS:
case SAFETY_STOP:
break;
case DECOSTOP:
deco = array_uint16_le (data + offset);
have_deco = 1;
break;
case GASMIX:
o2 = data[offset + 0];
he = data[offset + 1];
if (o2 != o2_previous || he != he_previous) {
// Find the gasmix in the list.
unsigned int i = 0;
while (i < ngasmixes) {
if (o2 == gasmix[i].oxygen && he == gasmix[i].helium)
break;
i++;
}
// Add it to list if not found.
if (i >= ngasmixes) {
if (i >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_DATAFORMAT;
}
gasmix[i].oxygen = o2;
gasmix[i].helium = he;
ngasmixes = i + 1;
}
o2_previous = o2;
he_previous = he;
gasmix_idx = i;
have_gasmix = 1;
}
break;
case SETPOINT:
setpoint = data[offset];
have_setpoint = 1;
break;
case TANK_PRESSURE:
tank_id = array_uint16_le (data + offset + 0);
tank_pressure = array_uint16_le (data + offset + 2);
if (tank_id != tank_id_previous) {
// Find the tank in the list.
unsigned int i = 0;
while (i < ntanks) {
if (tank_id == tank[i].id)
break;
i++;
}
// Add a new tank if necessary.
if (i >= ntanks) {
if (i >= NTANKS) {
ERROR (abstract->context, "Maximum number of tanks reached.");
return DC_STATUS_DATAFORMAT;
}
tank[i].id = tank_id;
tank[i].beginpressure = tank_pressure;
tank[i].endpressure = tank_pressure;
ntanks = i + 1;
}
tank_id_previous = tank_id;
tank_idx = i;
}
tank[tank_idx].endpressure = tank_pressure;
pressure[tank_idx] = tank_pressure;
have_pressure |= 1 << tank_idx;
break;
case TANK_LIST:
break;
default:
WARNING (abstract->context, "Unknown event %u", type);
break;
}
offset += length;
events++;
} else {
if (samples >= nsamples) {
break;
}
// Get the sample length.
unsigned int length = 2;
if (version == XEO_V1_A || version == XEO_V2_A ||
version == XEO_V3_A || version == KAON_V1 ||
version == KAON_V2) {
length += 14;
}
if (offset + length > size) {
ERROR (abstract->context, "Buffer overflow at offset %u", offset);
return DC_STATUS_DATAFORMAT;
}
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
// Depth (1/100 m).
sample.depth = value / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
// Temperature (1/10 °C).
int temperature = (signed short) array_uint16_le (data + offset);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
// Gas mix
if (have_gasmix) {
sample.gasmix = gasmix_idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
have_gasmix = 0;
}
// Setpoint (1/10 bar).
if (have_setpoint) {
sample.setpoint = setpoint / 10.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
have_setpoint = 0;
}
// Tank pressure (1/100 bar).
if (have_pressure) {
for (unsigned int i = 0; i < ntanks; ++i) {
if (have_pressure & (1 << i)) {
sample.pressure.tank = i;
sample.pressure.value = pressure[i] / 100.0;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
}
}
have_pressure = 0;
}
// Deco/ndl
if (have_deco) {
if (deco) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco / 100.0;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0;
}
sample.deco.time = 0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
have_deco = 0;
}
offset += length;
samples++;
}
}
// Cache the data for later use.
for (unsigned int i = 0; i < ntanks; ++i) {
parser->tank[i] = tank[i];
}
for (unsigned int i = 0; i < ngasmixes; ++i) {
parser->gasmix[i] = gasmix[i];
}
parser->ngasmixes = ngasmixes;
parser->ntanks = ntanks;
parser->cached = 1;
return DC_STATUS_SUCCESS;
}

View File

@ -176,6 +176,7 @@ static const oceanic_common_version_t oceanic_oc1_version[] = {
{"AQUAI550 \0\0 1024"},
{"AQUA550C \0\0 1024"},
{"WISDOM04 \0\0 1024"},
{"AQUA470C \0\0 1024"},
};
static const oceanic_common_version_t oceanic_oci_version[] = {

View File

@ -98,6 +98,7 @@
#define VEO40 0x4654
#define WISDOM4 0x4655
#define PROPLUS4 0x4656
#define I470TC 0x4743
#define NORMAL 0
#define GAUGE 1
@ -172,7 +173,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
model == I300 || model == I550 ||
model == I200 || model == I200C ||
model == I300C || model == GEO40 ||
model == VEO40) {
model == VEO40 || model == I470TC) {
parser->headersize -= PAGESIZE;
} else if (model == VT4 || model == VT41) {
parser->headersize += PAGESIZE;
@ -275,6 +276,7 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
case VISION:
case XPAIR:
case WISDOM4:
case I470TC:
datetime->year = ((p[5] & 0xE0) >> 5) + ((p[7] & 0xE0) >> 2) + 2000;
datetime->month = (p[3] & 0x0F);
datetime->day = ((p[0] & 0x80) >> 3) + ((p[3] & 0xF0) >> 4);
@ -488,6 +490,10 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
o2_offset = 0x30;
ngasmixes = 4;
o2_step = 2;
} else if (parser->model == I470TC) {
o2_offset = 0x28;
ngasmixes = 3;
o2_step = 2;
} else if (parser->model == WISDOM4) {
o2_offset = header + 4;
ngasmixes = 1;
@ -739,7 +745,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == TX1 || parser->model == A300CS ||
parser->model == VTX || parser->model == I450T ||
parser->model == I750TC || parser->model == PROPLUSX ||
parser->model == I770R) {
parser->model == I770R || parser->model == I470TC) {
samplesize = PAGESIZE;
}
@ -873,7 +879,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
}
// Time.
if (parser->model == I450T) {
if (parser->model == I450T || parser->model == I470TC) {
unsigned int minute = bcd2dec(data[offset + 0]);
unsigned int hour = bcd2dec(data[offset + 1] & 0x0F);
unsigned int second = bcd2dec(data[offset + 2]);
@ -914,7 +920,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == I450T || parser->model == I300 ||
parser->model == I200 || parser->model == I100 ||
parser->model == I300C || parser->model == I200C ||
parser->model == GEO40 || parser->model == VEO40) {
parser->model == GEO40 || parser->model == VEO40 ||
parser->model == I470TC) {
temperature = data[offset + 3];
} else if (parser->model == OCS || parser->model == TX1) {
temperature = data[offset + 1];
@ -956,7 +963,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
if (have_pressure) {
if (parser->model == OC1A || parser->model == OC1B ||
parser->model == OC1C || parser->model == OCI ||
parser->model == I450T)
parser->model == I450T || parser->model == I470TC)
pressure = (data[offset + 10] + (data[offset + 11] << 8)) & 0x0FFF;
else if (parser->model == VT4 || parser->model == VT41||
parser->model == ATOM3 || parser->model == ATOM31 ||
@ -989,7 +996,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == I450T || parser->model == I300 ||
parser->model == I200 || parser->model == I100 ||
parser->model == I300C || parser->model == I200C ||
parser->model == GEO40 || parser->model == VEO40)
parser->model == GEO40 || parser->model == VEO40 ||
parser->model == I470TC)
depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF;
else if (parser->model == ATOM1)
depth = data[offset + 3] * 16;
@ -1043,7 +1051,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == OC1C || parser->model == OCI ||
parser->model == I100 || parser->model == I300C ||
parser->model == I450T || parser->model == I200C ||
parser->model == GEO40 || parser->model == VEO40) {
parser->model == GEO40 || parser->model == VEO40 ||
parser->model == I470TC) {
decostop = (data[offset + 7] & 0xF0) >> 4;
decotime = array_uint16_le(data + offset + 6) & 0x0FFF;
have_deco = 1;
@ -1068,7 +1077,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
} else if (parser->model == I450T || parser->model == OC1A ||
parser->model == OC1B || parser->model == OC1C ||
parser->model == OCI || parser->model == PROPLUSX ||
parser->model == I770R) {
parser->model == I770R || parser->model == I470TC) {
rbt = array_uint16_le(data + offset + 8) & 0x01FF;
have_rbt = 1;
} else if (parser->model == VISION || parser->model == XPAIR ||

View File

@ -57,9 +57,12 @@
#include "divesystem_idive.h"
#include "cochran_commander.h"
#include "tecdiving_divecomputereu.h"
#include "mclean_extreme.h"
#include "liquivision_lynx.h"
// Not merged upstream yet
#include "garmin.h"
#include "deepblu.h"
#include "mclean_extreme.h"
#include "oceans_s1.h"
#include "context-private.h"
@ -176,20 +179,25 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_parser_create (&parser, context);
break;
case DC_FAMILY_MCLEAN_EXTREME:
rc = mclean_extreme_parser_create (&parser, context);
break;
case DC_FAMILY_LIQUIVISION_LYNX:
rc = liquivision_lynx_parser_create (&parser, context, model);
break;
default:
return DC_STATUS_INVALIDARGS;
// Not merged upstream yet
case DC_FAMILY_GARMIN:
rc = garmin_parser_create (&parser, context);
break;
case DC_FAMILY_DEEPBLU:
rc = deepblu_parser_create (&parser, context);
break;
case DC_FAMILY_MCLEAN_EXTREME:
rc = mclean_extreme_parser_create (&parser, context);
break;
case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_parser_create(&parser, context);
break;
default:
return DC_STATUS_INVALIDARGS;
}
*out = parser;

View File

@ -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;
}

View File

@ -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;
}

589
src/usb.c Normal file
View File

@ -0,0 +1,589 @@
/*
* 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 <stdlib.h>
#include <string.h>
#ifdef HAVE_LIBUSB
#ifdef _WIN32
#define NOGDI
#endif
#include <libusb.h>
#endif
#include <libdivecomputer/usb.h>
#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, &params)) {
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 || nbytes < 0) {
ERROR (abstract->context, "Usb read bulk transfer failed (%s).",
libusb_error_name (rc));
status = syserror (rc);
if (nbytes < 0)
nbytes = 0;
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 || nbytes < 0) {
ERROR (abstract->context, "Usb write bulk transfer failed (%s).",
libusb_error_name (rc));
status = syserror (rc);
if (nbytes < 0)
nbytes = 0;
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

View File

@ -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;
}
@ -703,10 +703,12 @@ dc_usbhid_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual
#if defined(USE_LIBUSB)
int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_in, data, size, &nbytes, usbhid->timeout);
if (rc != LIBUSB_SUCCESS) {
if (rc != LIBUSB_SUCCESS || nbytes < 0) {
ERROR (abstract->context, "Usb read interrupt transfer failed (%s).",
libusb_error_name (rc));
status = syserror (rc);
if (nbytes < 0)
nbytes = 0;
goto out;
}
#elif defined(USE_HIDAPI)
@ -749,10 +751,12 @@ dc_usbhid_write (dc_iostream_t *abstract, const void *data, size_t size, size_t
}
int rc = libusb_interrupt_transfer (usbhid->handle, usbhid->endpoint_out, (void *) buffer, length, &nbytes, 0);
if (rc != LIBUSB_SUCCESS) {
if (rc != LIBUSB_SUCCESS || nbytes < 0) {
ERROR (abstract->context, "Usb write interrupt transfer failed (%s).",
libusb_error_name (rc));
status = syserror (rc);
if (nbytes < 0)
nbytes = 0;
goto out;
}

View File

@ -477,8 +477,8 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
goto error_free;
}
// Set the timeout for receiving data (3000ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
// Set the timeout for receiving data (5000ms).
status = dc_iostream_set_timeout (device->iostream, 5000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;