From f107d7c0d8242e3f07b0b8ed5c9c041cc733c8f8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 8 Jun 2017 22:16:14 -0700 Subject: [PATCH 1/9] Add initial Scubapro G2 frontend The back-end parser seems to be the same as for the Uwatec Smart (aka Galileo Sol). At least that's the assumption right now. The downloader just uses USB HID (very similar to EON Steel) rather than the horrible IrDA thing. There's also eventually a BLE thing, but that's for the future. This is an unholy mixture of the Uwatec Smart downloader logic and the EON Steel usbhid transfer code. The back-end is pure Uwatec Smart (model 0x11, same as Galileo Sol). I'm not at all sure this gets everything right, but it downloads *something*. [Jef Driesen: Renamed the backend to uwatec, and made some smaller cosmetic changes to match the existing coding style.] Signed-off-by: Linus Torvalds --- examples/common.c | 1 + include/libdivecomputer/common.h | 1 + src/Makefile.am | 1 + src/descriptor.c | 6 + src/device.c | 4 + src/parser.c | 3 + src/uwatec_g2.c | 380 +++++++++++++++++++++++++++++++ src/uwatec_g2.h | 39 ++++ 8 files changed, 435 insertions(+) create mode 100644 src/uwatec_g2.c create mode 100644 src/uwatec_g2.h diff --git a/examples/common.c b/examples/common.c index 3c4561b..1024fde 100644 --- a/examples/common.c +++ b/examples/common.c @@ -55,6 +55,7 @@ static const backend_table_t g_backends[] = { {"memomouse", DC_FAMILY_UWATEC_MEMOMOUSE, 0}, {"smart", DC_FAMILY_UWATEC_SMART, 0x10}, {"meridian", DC_FAMILY_UWATEC_MERIDIAN, 0x20}, + {"g2", DC_FAMILY_UWATEC_G2, 0x11}, {"sensus", DC_FAMILY_REEFNET_SENSUS, 1}, {"sensuspro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, {"sensusultra", DC_FAMILY_REEFNET_SENSUSULTRA, 3}, diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 67398d1..293de3b 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -59,6 +59,7 @@ typedef enum dc_family_t { DC_FAMILY_UWATEC_MEMOMOUSE, DC_FAMILY_UWATEC_SMART, DC_FAMILY_UWATEC_MERIDIAN, + DC_FAMILY_UWATEC_G2, /* Oceanic */ DC_FAMILY_OCEANIC_VTPRO = (4 << 16), DC_FAMILY_OCEANIC_VEO250, diff --git a/src/Makefile.am b/src/Makefile.am index 60b5654..387b284 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,6 +38,7 @@ libdivecomputer_la_SOURCES = \ uwatec_memomouse.h uwatec_memomouse.c uwatec_memomouse_parser.c \ uwatec_smart.h uwatec_smart.c uwatec_smart_parser.c \ uwatec_meridian.h uwatec_meridian.c \ + uwatec_g2.h uwatec_g2.c \ oceanic_common.h oceanic_common.c \ oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \ oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \ diff --git a/src/descriptor.c b/src/descriptor.c index f0301bb..de4512e 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -135,6 +135,10 @@ static const dc_descriptor_t g_descriptors[] = { {"Scubapro", "Mantis", DC_FAMILY_UWATEC_MERIDIAN, 0x20}, {"Scubapro", "Chromis", DC_FAMILY_UWATEC_MERIDIAN, 0x24}, {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26}, + /* Scubapro G2 */ +#ifdef USBHID + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x11}, +#endif /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1}, {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2}, @@ -429,6 +433,8 @@ dc_descriptor_get_transport (dc_descriptor_t *descriptor) return DC_TRANSPORT_USB; else if (descriptor->type == DC_FAMILY_SUUNTO_EONSTEEL) return DC_TRANSPORT_USBHID; + else if (descriptor->type == DC_FAMILY_UWATEC_G2) + return DC_TRANSPORT_USBHID; else if (descriptor->type == DC_FAMILY_UWATEC_SMART) return DC_TRANSPORT_IRDA; else diff --git a/src/device.c b/src/device.c index 2f3e1af..9dcc528 100644 --- a/src/device.c +++ b/src/device.c @@ -33,6 +33,7 @@ #include "reefnet_sensuspro.h" #include "reefnet_sensusultra.h" #include "uwatec_aladin.h" +#include "uwatec_g2.h" #include "uwatec_memomouse.h" #include "uwatec_meridian.h" #include "uwatec_smart.h" @@ -138,6 +139,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_UWATEC_MERIDIAN: rc = uwatec_meridian_device_open (&device, context, name); break; + case DC_FAMILY_UWATEC_G2: + rc = uwatec_g2_device_open (&device, context); + break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_device_open (&device, context, name); break; diff --git a/src/parser.c b/src/parser.c index 9fdb1a9..47382e5 100644 --- a/src/parser.c +++ b/src/parser.c @@ -100,6 +100,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa case DC_FAMILY_UWATEC_MERIDIAN: rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); break; + case DC_FAMILY_UWATEC_G2: + rc = uwatec_smart_parser_create (&parser, context, 0x11, devtime, systime); + break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_parser_create (&parser, context, devtime, systime); break; diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c new file mode 100644 index 0000000..b946eca --- /dev/null +++ b/src/uwatec_g2.c @@ -0,0 +1,380 @@ +/* + * libdivecomputer + * + * Copyright (C) 2008 Jef Driesen + * (C) 2017 Linus Torvalds + * + * 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 // malloc, free +#include // strncmp, strstr + +#include "uwatec_g2.h" +#include "context-private.h" +#include "device-private.h" +#include "usbhid.h" +#include "array.h" + +#define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_g2_device_vtable) + +#define PACKET_SIZE 64 + +typedef struct uwatec_g2_device_t { + dc_device_t base; + dc_usbhid_t *usbhid; + unsigned int address; + unsigned int timestamp; + unsigned int devtime; + dc_ticks_t systime; +} uwatec_g2_device_t; + +static dc_status_t uwatec_g2_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size); +static dc_status_t uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); +static dc_status_t uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t uwatec_g2_device_close (dc_device_t *abstract); + +static const dc_device_vtable_t uwatec_g2_device_vtable = { + sizeof(uwatec_g2_device_t), + DC_FAMILY_UWATEC_G2, + uwatec_g2_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + uwatec_g2_device_dump, /* dump */ + uwatec_g2_device_foreach, /* foreach */ + uwatec_g2_device_close /* close */ +}; + +static dc_status_t +uwatec_g2_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); + +static int +receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size) +{ + while (size) { + unsigned char buf[PACKET_SIZE]; + size_t transferred = 0; + dc_status_t rc = DC_STATUS_SUCCESS; + unsigned int len = 0; + + rc = dc_usbhid_read (device->usbhid, buf, PACKET_SIZE, &transferred); + if (rc != DC_STATUS_SUCCESS) { + ERROR (device->base.context, "read interrupt transfer failed"); + return -1; + } + if (transferred != PACKET_SIZE) { + ERROR (device->base.context, "incomplete read interrupt transfer (got %zu, expected %d)", transferred, PACKET_SIZE); + return -1; + } + len = buf[0]; + if (len >= PACKET_SIZE) { + ERROR (device->base.context, "read interrupt transfer returns impossible packet size (%d)", len); + return -1; + } + HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); + if (len > size) { + ERROR (device->base.context, "receive result buffer too small - truncating"); + len = size; + } + memcpy(data, buf + 1, len); + size -= len; + data += len; + } + return 0; +} + + +static dc_status_t +uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize) +{ + unsigned char buf[PACKET_SIZE]; + dc_status_t status = DC_STATUS_SUCCESS; + size_t transferred = 0; + + if (csize >= PACKET_SIZE) { + ERROR (device->base.context, "command too big (%d)", csize); + return DC_STATUS_INVALIDARGS; + } + + buf[0] = csize; + memcpy(buf + 1, command, csize); + status = dc_usbhid_write (device->usbhid, buf, csize + 1, &transferred); + if (status != DC_STATUS_SUCCESS) { + ERROR (device->base.context, "Failed to send the command."); + return status; + } + + if (receive_data (device, answer, asize) < 0) { + ERROR (device->base.context, "Failed to receive the answer."); + return DC_STATUS_IO; + } + + return DC_STATUS_SUCCESS; +} + + +dc_status_t +uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) +{ + dc_status_t status = DC_STATUS_SUCCESS; + uwatec_g2_device_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (uwatec_g2_device_t *) dc_device_allocate (context, &uwatec_g2_device_vtable); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Set the default values. + device->usbhid = NULL; + device->address = 0; + device->timestamp = 0; + device->systime = (dc_ticks_t) -1; + device->devtime = 0; + + // Open the irda socket. + status = dc_usbhid_open (&device->usbhid, context, 0x2e6c, 0x3201); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to open USB device"); + goto error_free; + } + + *out = (dc_device_t*) device; + + return DC_STATUS_SUCCESS; + +error_free: + dc_device_deallocate ((dc_device_t *) device); + return status; +} + + +static dc_status_t +uwatec_g2_device_close (dc_device_t *abstract) +{ + dc_status_t status = DC_STATUS_SUCCESS; + uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; + + // Close the device. + rc = dc_usbhid_close (device->usbhid); + if (status != DC_STATUS_SUCCESS) { + dc_status_set_error(&status, rc); + } + + return status; +} + + +static dc_status_t +uwatec_g2_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract; + + if (size && size != 4) + return DC_STATUS_INVALIDARGS; + + if (size) + device->timestamp = array_uint32_le (data); + else + device->timestamp = 0; + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) +{ + uwatec_g2_device_t *device = (uwatec_g2_device_t*) abstract; + dc_status_t rc = DC_STATUS_SUCCESS; + + // Erase the current contents of the buffer. + if (!dc_buffer_clear (buffer)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + + // Read the model number. + unsigned char cmd_model[1] = {0x10}; + unsigned char model[1] = {0}; + rc = uwatec_g2_transfer (device, cmd_model, sizeof (cmd_model), model, sizeof (model)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Read the serial number. + unsigned char cmd_serial[1] = {0x14}; + unsigned char serial[4] = {0}; + rc = uwatec_g2_transfer (device, cmd_serial, sizeof (cmd_serial), serial, sizeof (serial)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Read the device clock. + unsigned char cmd_devtime[1] = {0x1A}; + unsigned char devtime[4] = {0}; + rc = uwatec_g2_transfer (device, cmd_devtime, sizeof (cmd_devtime), devtime, sizeof (devtime)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Store the clock calibration values. + device->systime = dc_datetime_now (); + device->devtime = array_uint32_le (devtime); + + // Update and emit a progress event. + progress.current += 9; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + + // Emit a clock event. + dc_event_clock_t clock; + clock.systime = device->systime; + clock.devtime = device->devtime; + device_event_emit (&device->base, DC_EVENT_CLOCK, &clock); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = model[0]; + devinfo.firmware = 0; + devinfo.serial = array_uint32_le (serial); + device_event_emit (&device->base, DC_EVENT_DEVINFO, &devinfo); + + // Command template. + unsigned char command[9] = {0x00, + (device->timestamp ) & 0xFF, + (device->timestamp >> 8 ) & 0xFF, + (device->timestamp >> 16) & 0xFF, + (device->timestamp >> 24) & 0xFF, + 0x10, + 0x27, + 0, + 0}; + + // Data Length. + command[0] = 0xC6; + unsigned char answer[4] = {0}; + rc = uwatec_g2_transfer (device, command, sizeof (command), answer, sizeof (answer)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + unsigned int length = array_uint32_le (answer); + + // Update and emit a progress event. + progress.maximum = 4 + 9 + (length ? length + 4 : 0); + progress.current += 4; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + + if (length == 0) + return DC_STATUS_SUCCESS; + + // Allocate the required amount of memory. + if (!dc_buffer_resize (buffer, length)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + unsigned char *data = dc_buffer_get_data (buffer); + + // Data. + command[0] = 0xC4; + rc = uwatec_g2_transfer (device, command, sizeof (command), answer, sizeof (answer)); + if (rc != DC_STATUS_SUCCESS) + return rc; + + unsigned int total = array_uint32_le (answer); + + // Update and emit a progress event. + progress.current += 4; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + + if (total != length + 4) { + ERROR (abstract->context, "Received an unexpected size."); + return DC_STATUS_PROTOCOL; + } + + if (receive_data (device, data, length)) { + ERROR (abstract->context, "Failed to receive the answer."); + return DC_STATUS_IO; + } + + // Update and emit a progress event. + progress.current += length; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +uwatec_g2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + dc_buffer_t *buffer = dc_buffer_new (0); + if (buffer == NULL) + return DC_STATUS_NOMEMORY; + + dc_status_t rc = uwatec_g2_device_dump (abstract, buffer); + if (rc != DC_STATUS_SUCCESS) { + dc_buffer_free (buffer); + return rc; + } + + rc = uwatec_g2_extract_dives (abstract, + dc_buffer_get_data (buffer), dc_buffer_get_size (buffer), callback, userdata); + + dc_buffer_free (buffer); + + return rc; +} + + +static dc_status_t +uwatec_g2_extract_dives (dc_device_t *abstract, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata) +{ + if (abstract && !ISINSTANCE (abstract)) + return DC_STATUS_INVALIDARGS; + + const unsigned char header[4] = {0xa5, 0xa5, 0x5a, 0x5a}; + + // Search the data stream for start markers. + unsigned int previous = size; + unsigned int current = (size >= 4 ? size - 4 : 0); + while (current > 0) { + current--; + if (memcmp (data + current, header, sizeof (header)) == 0) { + // Get the length of the profile data. + unsigned int len = array_uint32_le (data + current + 4); + + // Check for a buffer overflow. + if (current + len > previous) + return DC_STATUS_DATAFORMAT; + + if (callback && !callback (data + current, len, data + current + 8, 4, userdata)) + return DC_STATUS_SUCCESS; + + // Prepare for the next dive. + previous = current; + current = (current >= 4 ? current - 4 : 0); + } + } + + return DC_STATUS_SUCCESS; +} diff --git a/src/uwatec_g2.h b/src/uwatec_g2.h new file mode 100644 index 0000000..c7ac7ca --- /dev/null +++ b/src/uwatec_g2.h @@ -0,0 +1,39 @@ +/* + * libdivecomputer + * + * Copyright (C) 2008 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 UWATEC_G2_H +#define UWATEC_G2_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +uwatec_g2_device_open (dc_device_t **device, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* UWATEC_G2_H */ From 4ef57b24bb2046b1a848d284ea0b98ceff3e5896 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2017 06:27:21 +0900 Subject: [PATCH 2/9] Scubapro G2: clean up unused field Jef Driesen correctly points out that the 'address' field is just leftover from the IrDA code, and is meaningless for the USB HID transport version of the Scubapro G2. Signed-off-by: Linus Torvalds --- src/uwatec_g2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index b946eca..864ec32 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -36,7 +36,6 @@ typedef struct uwatec_g2_device_t { dc_device_t base; dc_usbhid_t *usbhid; - unsigned int address; unsigned int timestamp; unsigned int devtime; dc_ticks_t systime; @@ -144,7 +143,6 @@ uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) // Set the default values. device->usbhid = NULL; - device->address = 0; device->timestamp = 0; device->systime = (dc_ticks_t) -1; device->devtime = 0; From 8c0ab03706d2f30f3f750502b6f17c9347c46075 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2017 06:36:57 +0900 Subject: [PATCH 3/9] Scubapro G2: add back the initial handshake code When doing the G2 downloader, I dropped the initial handshake as I tried to keep the code minimal, and the handshake didn't seem to make any difference what-so-ever to me. And it probably doesn't matter for anybody else either. But the code isn't working for some people, and maybe it does actually matter. More importantly, Scubapro's own LogTRAK application does send those two initial commands, and it's probably a good idea to minimize the differences between the different downloaders anyway, so add the handshake sequence back in. Signed-off-by: Linus Torvalds --- src/uwatec_g2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 864ec32..7fb8e0d 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -125,6 +125,43 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u } +static dc_status_t +uwatec_g2_handshake (uwatec_g2_device_t *device) +{ + dc_device_t *abstract = (dc_device_t *) device; + + // Command template. + unsigned char answer[1] = {0}; + unsigned char command[5] = {0x00, 0x10, 0x27, 0, 0}; + + // Handshake (stage 1). + command[0] = 0x1B; + dc_status_t rc = uwatec_g2_transfer (device, command, 1, answer, 1); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Verify the answer. + if (answer[0] != 0x01) { + ERROR (abstract->context, "Unexpected answer byte(s)."); + return DC_STATUS_PROTOCOL; + } + + // Handshake (stage 2). + command[0] = 0x1C; + rc = uwatec_g2_transfer (device, command, 5, answer, 1); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Verify the answer. + if (answer[0] != 0x01) { + ERROR (abstract->context, "Unexpected answer byte(s)."); + return DC_STATUS_PROTOCOL; + } + + return DC_STATUS_SUCCESS; +} + + dc_status_t uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) { @@ -154,10 +191,19 @@ uwatec_g2_device_open (dc_device_t **out, dc_context_t *context) goto error_free; } + // Perform the handshaking. + status = uwatec_g2_handshake (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to handshake with the device."); + goto error_close; + } + *out = (dc_device_t*) device; return DC_STATUS_SUCCESS; +error_close: + dc_usbhid_close (device->usbhid); error_free: dc_device_deallocate ((dc_device_t *) device); return status; From fcfee82cc875126db069fe2644cea08e42b921d0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 15 Jun 2017 07:01:51 +0900 Subject: [PATCH 4/9] Scubapro G2: add missed command packet logging I did the packet logging for the received data side, but forgot to do the same thing on the command transfer side, which makes the debug logs a bit less useful. Signed-off-by: Linus Torvalds --- src/uwatec_g2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 7fb8e0d..a1a2932 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -108,6 +108,8 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u return DC_STATUS_INVALIDARGS; } + HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", command, csize); + buf[0] = csize; memcpy(buf + 1, command, csize); status = dc_usbhid_write (device->usbhid, buf, csize + 1, &transferred); From eae071c97d1efb9971a6d77fba52ca6bdbcd1a4b Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 20 Jun 2017 21:02:01 +0200 Subject: [PATCH 5/9] Use the standard libdivecomputer error codes The error code returned by the dc_usbhid_read() function should be returned as-is, instead of being replaced with some generic error, which gets translated again to DC_STATUS_IO in the caller. --- src/uwatec_g2.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index a1a2932..2fa86c8 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -60,7 +60,7 @@ static const dc_device_vtable_t uwatec_g2_device_vtable = { static dc_status_t uwatec_g2_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); -static int +static dc_status_t receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size) { while (size) { @@ -72,16 +72,16 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size rc = dc_usbhid_read (device->usbhid, buf, PACKET_SIZE, &transferred); if (rc != DC_STATUS_SUCCESS) { ERROR (device->base.context, "read interrupt transfer failed"); - return -1; + return rc; } if (transferred != PACKET_SIZE) { ERROR (device->base.context, "incomplete read interrupt transfer (got %zu, expected %d)", transferred, PACKET_SIZE); - return -1; + return DC_STATUS_PROTOCOL; } len = buf[0]; if (len >= PACKET_SIZE) { ERROR (device->base.context, "read interrupt transfer returns impossible packet size (%d)", len); - return -1; + return DC_STATUS_PROTOCOL; } HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); if (len > size) { @@ -92,7 +92,7 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size size -= len; data += len; } - return 0; + return DC_STATUS_SUCCESS; } @@ -118,9 +118,10 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u return status; } - if (receive_data (device, answer, asize) < 0) { + status = receive_data (device, answer, asize); + if (status != DC_STATUS_SUCCESS) { ERROR (device->base.context, "Failed to receive the answer."); - return DC_STATUS_IO; + return status; } return DC_STATUS_SUCCESS; @@ -357,9 +358,10 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_PROTOCOL; } - if (receive_data (device, data, length)) { + rc = receive_data (device, data, length); + if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); - return DC_STATUS_IO; + return rc; } // Update and emit a progress event. From 06259fed1937ac3663922c5e1eebfa118933293c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 20 Jun 2017 21:25:36 +0200 Subject: [PATCH 6/9] Abort with an error if the buffer is too small Silently truncating the data packet if the buffer is too small will result in a corrupt data stream. --- src/uwatec_g2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 2fa86c8..388b82c 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -85,8 +85,8 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size } HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", buf + 1, len); if (len > size) { - ERROR (device->base.context, "receive result buffer too small - truncating"); - len = size; + ERROR (device->base.context, "receive result buffer too small"); + return DC_STATUS_PROTOCOL; } memcpy(data, buf + 1, len); size -= len; From 7ee210f83f734c0d7dbac0df5a4ff91a5dcdba3e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 20 Jun 2017 21:36:47 +0200 Subject: [PATCH 7/9] Enable more fine grained progress events At the moment, the progress reporting will jump straight from about 0% at the start of the download to 100% at the end of the download, without any updates in between. This is improved by updating after every packet. --- src/uwatec_g2.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/uwatec_g2.c b/src/uwatec_g2.c index 388b82c..e3cc527 100644 --- a/src/uwatec_g2.c +++ b/src/uwatec_g2.c @@ -61,7 +61,7 @@ static dc_status_t uwatec_g2_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata); static dc_status_t -receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size) +receive_data (uwatec_g2_device_t *device, dc_event_progress_t *progress, unsigned char *data, unsigned int size) { while (size) { unsigned char buf[PACKET_SIZE]; @@ -88,6 +88,13 @@ receive_data (uwatec_g2_device_t *device, unsigned char *data, unsigned int size ERROR (device->base.context, "receive result buffer too small"); return DC_STATUS_PROTOCOL; } + + // Update and emit a progress event. + if (progress) { + progress->current += len; + device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); + } + memcpy(data, buf + 1, len); size -= len; data += len; @@ -118,7 +125,7 @@ uwatec_g2_transfer (uwatec_g2_device_t *device, const unsigned char command[], u return status; } - status = receive_data (device, answer, asize); + status = receive_data (device, NULL, answer, asize); if (status != DC_STATUS_SUCCESS) { ERROR (device->base.context, "Failed to receive the answer."); return status; @@ -358,16 +365,12 @@ uwatec_g2_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_PROTOCOL; } - rc = receive_data (device, data, length); + rc = receive_data (device, &progress, data, length); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return rc; } - // Update and emit a progress event. - progress.current += length; - device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress); - return DC_STATUS_SUCCESS; } From 0a5623a949eef1c12fb1ce39e4941b31f0c29eb1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Jun 2017 06:55:14 +0200 Subject: [PATCH 8/9] Use the correct model number The model number reported by the Scubapro G2 is 0x32. --- src/descriptor.c | 2 +- src/parser.c | 4 +--- src/uwatec_smart_parser.c | 8 ++++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index de4512e..9b537e1 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -137,7 +137,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_MERIDIAN, 0x26}, /* Scubapro G2 */ #ifdef USBHID - {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x11}, + {"Scubapro", "G2", DC_FAMILY_UWATEC_G2, 0x32}, #endif /* Reefnet */ {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1}, diff --git a/src/parser.c b/src/parser.c index 47382e5..29a5d41 100644 --- a/src/parser.c +++ b/src/parser.c @@ -98,10 +98,8 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa break; case DC_FAMILY_UWATEC_SMART: case DC_FAMILY_UWATEC_MERIDIAN: - rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); - break; case DC_FAMILY_UWATEC_G2: - rc = uwatec_smart_parser_create (&parser, context, 0x11, devtime, systime); + rc = uwatec_smart_parser_create (&parser, context, model, devtime, systime); break; case DC_FAMILY_REEFNET_SENSUS: rc = reefnet_sensus_parser_create (&parser, context, devtime, systime); diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index b350f1e..950757d 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -47,6 +47,7 @@ #define MERIDIAN 0x20 #define CHROMIS 0x24 #define MANTIS2 0x26 +#define G2 0x32 #define UNSUPPORTED 0xFFFFFFFF @@ -500,7 +501,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) divemode != DC_DIVEMODE_FREEDIVE) { if (parser->model == GALILEO || parser->model == GALILEOTRIMIX || parser->model == ALADIN2G || parser->model == MERIDIAN || - parser->model == CHROMIS || parser->model == MANTIS2) { + parser->model == CHROMIS || parser->model == MANTIS2 || + parser->model == G2) { unsigned int offset = header->tankpressure + 2 * i; endpressure = array_uint16_le(data + offset); beginpressure = array_uint16_le(data + offset + 2 * header->ngases); @@ -578,6 +580,7 @@ uwatec_smart_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i case MERIDIAN: case CHROMIS: case MANTIS2: + case G2: parser->headersize = 152; parser->header = &uwatec_smart_galileo_header; parser->samples = uwatec_smart_galileo_samples; @@ -928,7 +931,8 @@ uwatec_smart_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int id = 0; if (parser->model == GALILEO || parser->model == GALILEOTRIMIX || parser->model == ALADIN2G || parser->model == MERIDIAN || - parser->model == CHROMIS || parser->model == MANTIS2) { + parser->model == CHROMIS || parser->model == MANTIS2 || + parser->model == G2) { // Uwatec Galileo id = uwatec_galileo_identify (data[offset]); } else { From 3a05e0ecf022a35ff62f055e1775a80c272c7d81 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 23 Jun 2017 06:56:48 +0200 Subject: [PATCH 9/9] Use the trimix data format --- src/uwatec_smart_parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 950757d..99bfa28 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -440,6 +440,8 @@ uwatec_smart_parser_cache (uwatec_smart_parser_t *parser) parser->events[2] = uwatec_smart_galileo_events_2; parser->nevents[2] = C_ARRAY_SIZE (uwatec_smart_galileo_events_2); } + } else if (parser->model == G2) { + trimix = 1; } // Get the settings.