From 872f5a594de78113a21b2c5c75bd9a0c402a93cd Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Aug 2009 09:52:50 +0000 Subject: [PATCH] Add a common base class. The transfer protocol of the Suunto Vyper 2 and D9 devices is very similar, which allows to share most of the transfer code between the backends. --- msvc/libdivecomputer.vcproj | 8 + src/Makefile.am | 1 + src/suunto_common2.c | 414 +++++++++++++++++++++++++++++++ src/suunto_common2.h | 68 +++++ src/suunto_d9.c | 481 ++---------------------------------- src/suunto_vyper2.c | 479 ++--------------------------------- 6 files changed, 534 insertions(+), 917 deletions(-) create mode 100644 src/suunto_common2.c create mode 100644 src/suunto_common2.h diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index b267ddd..db32a53 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -260,6 +260,10 @@ RelativePath="..\src\suunto_common.c" > + + @@ -414,6 +418,10 @@ RelativePath="..\src\suunto_common.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 07f7fa2..df450da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,6 +45,7 @@ libdivecomputer_la_SOURCES = \ parser.h parser-private.h parser.c \ suunto.h \ suunto_common.h suunto_common.c \ + suunto_common2.h suunto_common2.c \ suunto_solution.h suunto_solution.c suunto_solution_parser.c \ suunto_eon.h suunto_eon.c \ suunto_vyper.h suunto_vyper.c suunto_vyper_parser.c suunto_spyder_parser.c \ diff --git a/src/suunto_common2.c b/src/suunto_common2.c new file mode 100644 index 0000000..646188c --- /dev/null +++ b/src/suunto_common2.c @@ -0,0 +1,414 @@ +/* + * libdivecomputer + * + * Copyright (C) 2009 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 // memcmp, memcpy +#include // assert + +#include "suunto_common2.h" +#include "utils.h" +#include "ringbuffer.h" +#include "checksum.h" +#include "array.h" + +#define WARNING(expr) \ +{ \ + message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ +} + +#define MAXRETRIES 2 + +#define SZ_VERSION 0x04 +#define SZ_MEMORY 0x8000 +#define SZ_PACKET 0x78 +#define SZ_MINIMUM 8 + +#define FP_OFFSET 0x15 + +#define RB_PROFILE_BEGIN 0x019A +#define RB_PROFILE_END SZ_MEMORY - 2 +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, RB_PROFILE_BEGIN, RB_PROFILE_END) + +#define BACKEND(abstract) ((suunto_common2_device_backend_t *) abstract->backend) + +void +suunto_common2_device_init (suunto_common2_device_t *device, const suunto_common2_device_backend_t *backend) +{ + assert (device != NULL); + + // Initialize the base class. + device_init (&device->base, &backend->base); + + // Set the default values. + memset (device->fingerprint, 0, sizeof (device->fingerprint)); +} + + +static device_status_t +suunto_common2_transfer (device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) +{ + assert (asize >= size + 4); + + if (BACKEND (abstract)->packet == NULL) + return DEVICE_STATUS_UNSUPPORTED; + + // Occasionally, the dive computer does not respond to a command. + // In that case we retry the command a number of times before + // returning an error. Usually the dive computer will respond + // again during one of the retries. + + unsigned int nretries = 0; + device_status_t rc = DEVICE_STATUS_SUCCESS; + while ((rc = BACKEND (abstract)->packet (abstract, command, csize, answer, asize, size)) != DEVICE_STATUS_SUCCESS) { + // Automatically discard a corrupted packet, + // and request a new one. + if (rc != DEVICE_STATUS_TIMEOUT && rc != DEVICE_STATUS_PROTOCOL) + return rc; + + // Abort if the maximum number of retries is reached. + if (nretries++ >= MAXRETRIES) + return rc; + } + + return rc; +} + + +device_status_t +suunto_common2_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) +{ + suunto_common2_device_t *device = (suunto_common2_device_t*) abstract; + + if (size && size != sizeof (device->fingerprint)) + return DEVICE_STATUS_ERROR; + + if (size) + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + else + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +suunto_common2_device_version (device_t *abstract, unsigned char data[], unsigned int size) +{ + if (size < SZ_VERSION) { + WARNING ("Insufficient buffer space available."); + return DEVICE_STATUS_MEMORY; + } + + unsigned char answer[SZ_VERSION + 4] = {0}; + unsigned char command[4] = {0x0F, 0x00, 0x00, 0x0F}; + device_status_t rc = suunto_common2_transfer (abstract, command, sizeof (command), answer, sizeof (answer), 4); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + memcpy (data, answer + 3, SZ_VERSION); + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +suunto_common2_device_reset_maxdepth (device_t *abstract) +{ + unsigned char answer[4] = {0}; + unsigned char command[4] = {0x20, 0x00, 0x00, 0x20}; + device_status_t rc = suunto_common2_transfer (abstract, command, sizeof (command), answer, sizeof (answer), 0); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +suunto_common2_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size, device_progress_t *progress) +{ + // The data transmission is split in packages + // of maximum $SZ_PACKET bytes. + + unsigned int nbytes = 0; + while (nbytes < size) { + // Calculate the package size. + unsigned int len = size - nbytes; + if (len > SZ_PACKET) + len = SZ_PACKET; + + // Read the package. + unsigned char answer[SZ_PACKET + 7] = {0}; + unsigned char command[7] = {0x05, 0x00, 0x03, + (address >> 8) & 0xFF, // high + (address ) & 0xFF, // low + len, // count + 0}; // CRC + command[6] = checksum_xor_uint8 (command, 6, 0x00); + device_status_t rc = suunto_common2_transfer (abstract, command, sizeof (command), answer, len + 7, len); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + memcpy (data, answer + 6, len); + + // Update and emit a progress event. + if (progress) { + progress->current += len; + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, progress); + } + + nbytes += len; + address += len; + data += len; + } + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +suunto_common2_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) +{ + return suunto_common2_read (abstract, address, data, size, NULL); +} + + +device_status_t +suunto_common2_device_write (device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size) +{ + // The data transmission is split in packages + // of maximum $SZ_PACKET bytes. + + unsigned int nbytes = 0; + while (nbytes < size) { + // Calculate the package size. + unsigned int len = size - nbytes; + if (len > SZ_PACKET) + len = SZ_PACKET; + + // Write the package. + unsigned char answer[7] = {0}; + unsigned char command[SZ_PACKET + 7] = {0x06, 0x00, len + 3, + (address >> 8) & 0xFF, // high + (address ) & 0xFF, // low + len, // count + 0}; // data + CRC + memcpy (command + 6, data, len); + command[len + 6] = checksum_xor_uint8 (command, len + 6, 0x00); + device_status_t rc = suunto_common2_transfer (abstract, command, len + 7, answer, sizeof (answer), 0); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + nbytes += len; + address += len; + data += len; + } + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +suunto_common2_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result) +{ + if (size < SZ_MEMORY) { + WARNING ("Insufficient buffer space available."); + return DEVICE_STATUS_MEMORY; + } + + // Enable progress notifications. + device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; + progress.maximum = SZ_MEMORY; + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + device_status_t rc = suunto_common2_read (abstract, 0x00, data, SZ_MEMORY, &progress); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + if (result) + *result = SZ_MEMORY; + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +suunto_common2_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) +{ + suunto_common2_device_t *device = (suunto_common2_device_t*) abstract; + + // Enable progress notifications. + device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; + progress.maximum = RB_PROFILE_END - RB_PROFILE_BEGIN + 8 + SZ_VERSION + (SZ_MINIMUM > 4 ? SZ_MINIMUM : 4); + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // Read the version info. + unsigned char version[SZ_VERSION] = {0}; + device_status_t rc = suunto_common2_device_version (abstract, version, sizeof (version)); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read memory header."); + return rc; + } + + // Update and emit a progress event. + progress.current += sizeof (version); + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // Read the serial number. + unsigned char serial[SZ_MINIMUM > 4 ? SZ_MINIMUM : 4] = {0}; + rc = suunto_common2_read (abstract, 0x0023, serial, sizeof (serial), NULL); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read memory header."); + return rc; + } + + // Update and emit a progress event. + progress.current += sizeof (serial); + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // Emit a device info event. + device_devinfo_t devinfo; + devinfo.model = version[0]; + devinfo.firmware = array_uint24_be (version + 1); + devinfo.serial = array_uint32_be (serial); + device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo); + + // Read the header bytes. + unsigned char header[8] = {0}; + rc = suunto_common2_read (abstract, 0x0190, header, sizeof (header), NULL); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read memory header."); + return rc; + } + + // Obtain the pointers from the header. + unsigned int last = array_uint16_le (header + 0); + unsigned int count = array_uint16_le (header + 2); + unsigned int end = array_uint16_le (header + 4); + unsigned int begin = array_uint16_le (header + 6); + + // Memory buffer to store all the dives. + + unsigned char data[SZ_MINIMUM + RB_PROFILE_END - RB_PROFILE_BEGIN] = {0}; + + // Calculate the total amount of bytes. + + unsigned int remaining = RB_PROFILE_DISTANCE (begin, end); + + // Update and emit a progress event. + + progress.maximum -= (RB_PROFILE_END - RB_PROFILE_BEGIN) - remaining; + progress.current += sizeof (header); + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // To reduce the number of read operations, we always try to read + // packages with the largest possible size. As a consequence, the + // last package of a dive can contain data from more than one dive. + // Therefore, the remaining data of this package (and its size) + // needs to be preserved for the next dive. + + unsigned int available = 0; + + // The ring buffer is traversed backwards to retrieve the most recent + // dives first. This allows you to download only the new dives. During + // the traversal, the current pointer does always point to the end of + // the dive data and we move to the "next" dive by means of the previous + // pointer. + + unsigned int ndives = 0; + unsigned int current = end; + unsigned int previous = last; + while (current != begin) { + // Calculate the size of the current dive. + unsigned int size = RB_PROFILE_DISTANCE (previous, current); + + assert (size >= 4 && size <= remaining); + + unsigned int nbytes = available; + unsigned int address = current - available; + while (nbytes < size) { + // Calculate the package size. Try with the largest possible + // size first, and adjust when the end of the ringbuffer or + // the end of the profile data is reached. + unsigned int len = SZ_PACKET; + if (RB_PROFILE_BEGIN + len > address) + len = address - RB_PROFILE_BEGIN; // End of ringbuffer. + if (nbytes + len > remaining) + len = remaining - nbytes; // End of profile. + /*if (nbytes + len > size) + len = size - nbytes;*/ // End of dive (for testing only). + + // Always read at least the minimum amount of bytes, because + // reading fewer bytes is unreliable. The memory buffer is + // large enough to prevent buffer overflows, and the extra + // bytes are automatically ignored (due to reading backwards). + unsigned int extra = 0; + if (len < SZ_MINIMUM) + extra = SZ_MINIMUM - len; + + // Read the package. + unsigned char *p = data + SZ_MINIMUM + remaining - nbytes; + rc = suunto_common2_read (abstract, address - (len + extra), p - (len + extra), len + extra, NULL); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read memory."); + return rc; + } + + // Update and emit a progress event. + progress.current += len; + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // Next package. + nbytes += len; + address -= len; + if (address <= RB_PROFILE_BEGIN) + address = RB_PROFILE_END; + } + + // The last package of the current dive contains the previous and + // next pointers (in a continuous memory area). It can also contain + // a number of bytes from the next dive. The offset to the pointers + // is equal to the number of bytes remaining after the current dive. + + remaining -= size; + available = nbytes - size; + + unsigned int oprevious = array_uint16_le (data + SZ_MINIMUM + remaining + 0); + unsigned int onext = array_uint16_le (data + SZ_MINIMUM + remaining + 2); + assert (current == onext); + + // Next dive. + current = previous; + previous = oprevious; + ndives++; + + unsigned int offset = SZ_MINIMUM + remaining; + if (memcmp (data + offset + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) + return DEVICE_STATUS_SUCCESS; + + if (callback && !callback (data + offset + 4, size - 4, userdata)) + return DEVICE_STATUS_SUCCESS; + } + assert (remaining == 0); + assert (available == 0); + assert (ndives == count); + + return DEVICE_STATUS_SUCCESS; +} diff --git a/src/suunto_common2.h b/src/suunto_common2.h new file mode 100644 index 0000000..91ac48c --- /dev/null +++ b/src/suunto_common2.h @@ -0,0 +1,68 @@ +/* + * libdivecomputer + * + * Copyright (C) 2009 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 SUUNTO_COMMON2_H +#define SUUNTO_COMMON2_H + +#include "device-private.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct suunto_common2_device_t { + device_t base; + unsigned char fingerprint[7]; +} suunto_common2_device_t; + +typedef struct suunto_common2_device_backend_t { + device_backend_t base; + device_status_t (*packet) (device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size); +} suunto_common2_device_backend_t; + +void +suunto_common2_device_init (suunto_common2_device_t *device, const suunto_common2_device_backend_t *backend); + +device_status_t +suunto_common2_device_set_fingerprint (device_t *device, const unsigned char data[], unsigned int size); + +device_status_t +suunto_common2_device_version (device_t *device, unsigned char data[], unsigned int size); + +device_status_t +suunto_common2_device_read (device_t *device, unsigned int address, unsigned char data[], unsigned int size); + +device_status_t +suunto_common2_device_write (device_t *device, unsigned int address, const unsigned char data[], unsigned int size); + +device_status_t +suunto_common2_device_dump (device_t *device, unsigned char data[], unsigned int size, unsigned int *result); + +device_status_t +suunto_common2_device_foreach (device_t *device, dive_callback_t callback, void *userdata); + +device_status_t +suunto_common2_device_reset_maxdepth (device_t *device); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* SUUNTO_COMMON2_H */ diff --git a/src/suunto_d9.c b/src/suunto_d9.c index dd226d4..ae3f08b 100644 --- a/src/suunto_d9.c +++ b/src/suunto_d9.c @@ -23,19 +23,13 @@ #include // malloc, free #include // assert -#include "device-private.h" +#include "suunto_common2.h" #include "suunto_d9.h" #include "serial.h" #include "utils.h" -#include "ringbuffer.h" #include "checksum.h" #include "array.h" -#define MAXRETRIES 2 - -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - #define WARNING(expr) \ { \ message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ @@ -46,38 +40,26 @@ rc == -1 ? DEVICE_STATUS_IO : DEVICE_STATUS_TIMEOUT \ ) -#define MINIMUM 8 - -#define RB_PROFILE_BEGIN 0x019A -#define RB_PROFILE_END SUUNTO_D9_MEMORY_SIZE - 2 -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, RB_PROFILE_BEGIN, RB_PROFILE_END) - -#define FP_OFFSET 0x15 -#define FP_SIZE 7 - typedef struct suunto_d9_device_t { - device_t base; + suunto_common2_device_t base; struct serial *port; - unsigned char fingerprint[FP_SIZE]; } suunto_d9_device_t; -static device_status_t suunto_d9_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size); -static device_status_t suunto_d9_device_version (device_t *abstract, unsigned char data[], unsigned int size); -static device_status_t suunto_d9_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); -static device_status_t suunto_d9_device_write (device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size); -static device_status_t suunto_d9_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result); -static device_status_t suunto_d9_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata); +static device_status_t suunto_d9_device_packet (device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size); static device_status_t suunto_d9_device_close (device_t *abstract); -static const device_backend_t suunto_d9_device_backend = { - DEVICE_TYPE_SUUNTO_D9, - suunto_d9_device_set_fingerprint, /* set_fingerprint */ - suunto_d9_device_version, /* version */ - suunto_d9_device_read, /* read */ - suunto_d9_device_write, /* write */ - suunto_d9_device_dump, /* dump */ - suunto_d9_device_foreach, /* foreach */ - suunto_d9_device_close /* close */ +static const suunto_common2_device_backend_t suunto_d9_device_backend = { + { + DEVICE_TYPE_SUUNTO_D9, + suunto_common2_device_set_fingerprint, /* set_fingerprint */ + suunto_common2_device_version, /* version */ + suunto_common2_device_read, /* read */ + suunto_common2_device_write, /* write */ + suunto_common2_device_dump, /* dump */ + suunto_common2_device_foreach, /* foreach */ + suunto_d9_device_close /* close */ + }, + suunto_d9_device_packet }; static int @@ -86,7 +68,7 @@ device_is_suunto_d9 (device_t *abstract) if (abstract == NULL) return 0; - return abstract->backend == &suunto_d9_device_backend; + return abstract->backend == (const device_backend_t *) &suunto_d9_device_backend; } @@ -104,11 +86,10 @@ suunto_d9_device_open (device_t **out, const char* name) } // Initialize the base class. - device_init (&device->base, &suunto_d9_device_backend); + suunto_common2_device_init (&device->base, &suunto_d9_device_backend); // Set the default values. device->port = NULL; - memset (device->fingerprint, 0, FP_SIZE); // Open the device. int rc = serial_open (&device->port, name); @@ -177,8 +158,10 @@ suunto_d9_device_close (device_t *abstract) static device_status_t -suunto_d9_send (suunto_d9_device_t *device, const unsigned char command[], unsigned int csize) +suunto_d9_device_packet (device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) { + suunto_d9_device_t *device = (suunto_d9_device_t *) abstract; + // Clear RTS to send the command. serial_set_rts (device->port, 0); @@ -205,22 +188,8 @@ suunto_d9_send (suunto_d9_device_t *device, const unsigned char command[], unsig // Set RTS to receive the reply. serial_set_rts (device->port, 1); - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_d9_packet (suunto_d9_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) -{ - // Send the command to the dive computer. - device_status_t rc = suunto_d9_send (device, command, csize); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Failed to send the command."); - return rc; - } - // Receive the answer of the dive computer. - int n = serial_read (device->port, answer, asize); + n = serial_read (device->port, answer, asize); if (n != asize) { WARNING ("Failed to receive the answer."); return EXITCODE (n); @@ -256,417 +225,11 @@ suunto_d9_packet (suunto_d9_device_t *device, const unsigned char command[], uns } -static device_status_t -suunto_d9_transfer (suunto_d9_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) -{ - assert (asize >= size + 4); - - // Occasionally, the dive computer does not respond to a command. - // In that case we retry the command a number of times before - // returning an error. Usually the dive computer will respond - // again during one of the retries. - - unsigned int nretries = 0; - device_status_t rc = DEVICE_STATUS_SUCCESS; - while ((rc = suunto_d9_packet (device, command, csize, answer, asize, size)) != DEVICE_STATUS_SUCCESS) { - // Automatically discard a corrupted packet, - // and request a new one. - if (rc != DEVICE_STATUS_TIMEOUT && rc != DEVICE_STATUS_PROTOCOL) - return rc; - - // Abort if the maximum number of retries is reached. - if (nretries++ >= MAXRETRIES) - return rc; - } - - return rc; -} - - -static device_status_t -suunto_d9_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) -{ - suunto_d9_device_t *device = (suunto_d9_device_t*) abstract; - - if (! device_is_suunto_d9 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - if (size && size != FP_SIZE) - return DEVICE_STATUS_ERROR; - - if (size) - memcpy (device->fingerprint, data, FP_SIZE); - else - memset (device->fingerprint, 0, FP_SIZE); - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_d9_device_version (device_t *abstract, unsigned char data[], unsigned int size) -{ - suunto_d9_device_t *device = (suunto_d9_device_t*) abstract; - - if (! device_is_suunto_d9 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - if (size < SUUNTO_D9_VERSION_SIZE) { - WARNING ("Insufficient buffer space available."); - return DEVICE_STATUS_MEMORY; - } - - unsigned char answer[SUUNTO_D9_VERSION_SIZE + 4] = {0}; - unsigned char command[4] = {0x0F, 0x00, 0x00, 0x0F}; - device_status_t rc = suunto_d9_transfer (device, command, sizeof (command), answer, sizeof (answer), 4); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - - memcpy (data, answer + 3, SUUNTO_D9_VERSION_SIZE); - -#ifndef NDEBUG - message ("D9ReadVersion()=\"%02x %02x %02x %02x\"\n", data[0], data[1], data[2], data[3]); -#endif - - return DEVICE_STATUS_SUCCESS; -} - - device_status_t suunto_d9_device_reset_maxdepth (device_t *abstract) { - suunto_d9_device_t *device = (suunto_d9_device_t*) abstract; - if (! device_is_suunto_d9 (abstract)) return DEVICE_STATUS_TYPE_MISMATCH; - unsigned char answer[4] = {0}; - unsigned char command[4] = {0x20, 0x00, 0x00, 0x20}; - device_status_t rc = suunto_d9_transfer (device, command, sizeof (command), answer, sizeof (answer), 0); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - -#ifndef NDEBUG - message ("D9ResetMaxDepth()\n"); -#endif - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_d9_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size, device_progress_t *progress) -{ - suunto_d9_device_t *device = (suunto_d9_device_t*) abstract; - - if (! device_is_suunto_d9 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - // The data transmission is split in packages - // of maximum $SUUNTO_D9_PACKET_SIZE bytes. - - unsigned int nbytes = 0; - while (nbytes < size) { - // Calculate the package size. - unsigned int len = MIN (size - nbytes, SUUNTO_D9_PACKET_SIZE); - - // Read the package. - unsigned char answer[SUUNTO_D9_PACKET_SIZE + 7] = {0}; - unsigned char command[7] = {0x05, 0x00, 0x03, - (address >> 8) & 0xFF, // high - (address ) & 0xFF, // low - len, // count - 0}; // CRC - command[6] = checksum_xor_uint8 (command, 6, 0x00); - device_status_t rc = suunto_d9_transfer (device, command, sizeof (command), answer, len + 7, len); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - - memcpy (data, answer + 6, len); - -#ifndef NDEBUG - message ("D9Read(0x%04x,%d)=\"", address, len); - for (unsigned int i = 0; i < len; ++i) { - message("%02x", data[i]); - } - message("\"\n"); -#endif - - // Update and emit a progress event. - if (progress) { - progress->current += len; - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, progress); - } - - nbytes += len; - address += len; - data += len; - } - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_d9_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) -{ - return suunto_d9_read (abstract, address, data, size, NULL); -} - - -static device_status_t -suunto_d9_device_write (device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size) -{ - suunto_d9_device_t *device = (suunto_d9_device_t*) abstract; - - if (! device_is_suunto_d9 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - // The data transmission is split in packages - // of maximum $SUUNTO_D9_PACKET_SIZE bytes. - - unsigned int nbytes = 0; - while (nbytes < size) { - // Calculate the package size. - unsigned int len = MIN (size - nbytes, SUUNTO_D9_PACKET_SIZE); - - // Write the package. - unsigned char answer[7] = {0}; - unsigned char command[SUUNTO_D9_PACKET_SIZE + 7] = {0x06, 0x00, len + 3, - (address >> 8) & 0xFF, // high - (address ) & 0xFF, // low - len, // count - 0}; // data + CRC - memcpy (command + 6, data, len); - command[len + 6] = checksum_xor_uint8 (command, len + 6, 0x00); - device_status_t rc = suunto_d9_transfer (device, command, len + 7, answer, sizeof (answer), 0); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - -#ifndef NDEBUG - message ("D9Write(0x%04x,%d,\"", address, len); - for (unsigned int i = 0; i < len; ++i) { - message ("%02x", data[i]); - } - message ("\");\n"); -#endif - - nbytes += len; - address += len; - data += len; - } - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_d9_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result) -{ - if (! device_is_suunto_d9 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - if (size < SUUNTO_D9_MEMORY_SIZE) { - WARNING ("Insufficient buffer space available."); - return DEVICE_STATUS_MEMORY; - } - - // Enable progress notifications. - device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; - progress.maximum = SUUNTO_D9_MEMORY_SIZE; - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - device_status_t rc = suunto_d9_read (abstract, 0x00, data, SUUNTO_D9_MEMORY_SIZE, &progress); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - - if (result) - *result = SUUNTO_D9_MEMORY_SIZE; - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_d9_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) -{ - suunto_d9_device_t *device = (suunto_d9_device_t*) abstract; - - if (! device_is_suunto_d9 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - // Enable progress notifications. - device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; - progress.maximum = RB_PROFILE_END - RB_PROFILE_BEGIN + 8 + 4 + (MINIMUM > 4 ? MINIMUM : 4); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Read the version info. - unsigned char version[4] = {0}; - device_status_t rc = suunto_d9_device_version (abstract, version, sizeof (version)); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory header."); - return rc; - } - - // Update and emit a progress event. - progress.current += sizeof (version); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Read the serial number. - unsigned char serial[MINIMUM > 4 ? MINIMUM : 4] = {0}; - rc = suunto_d9_read (abstract, 0x0023, serial, sizeof (serial), NULL); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory header."); - return rc; - } - - // Update and emit a progress event. - progress.current += sizeof (serial); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Emit a device info event. - device_devinfo_t devinfo; - devinfo.model = version[0]; - devinfo.firmware = array_uint24_be (version + 1); - devinfo.serial = array_uint32_be (serial); - device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo); - - // Read the header bytes. - unsigned char header[8] = {0}; - rc = suunto_d9_read (abstract, 0x0190, header, sizeof (header), NULL); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory header."); - return rc; - } - - // Obtain the pointers from the header. - unsigned int last = array_uint16_le (header + 0); - unsigned int count = array_uint16_le (header + 2); - unsigned int end = array_uint16_le (header + 4); - unsigned int begin = array_uint16_le (header + 6); - message ("Pointers: begin=%04x, last=%04x, end=%04x, count=%i\n", begin, last, end, count); - - // Memory buffer to store all the dives. - - unsigned char data[MINIMUM + RB_PROFILE_END - RB_PROFILE_BEGIN] = {0}; - - // Calculate the total amount of bytes. - - unsigned int remaining = RB_PROFILE_DISTANCE (begin, end); - - // Update and emit a progress event. - - progress.maximum -= (RB_PROFILE_END - RB_PROFILE_BEGIN) - remaining; - progress.current += sizeof (header); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // To reduce the number of read operations, we always try to read - // packages with the largest possible size. As a consequence, the - // last package of a dive can contain data from more than one dive. - // Therefore, the remaining data of this package (and its size) - // needs to be preserved for the next dive. - - unsigned int available = 0; - - // The ring buffer is traversed backwards to retrieve the most recent - // dives first. This allows you to download only the new dives. During - // the traversal, the current pointer does always point to the end of - // the dive data and we move to the "next" dive by means of the previous - // pointer. - - unsigned int ndives = 0; - unsigned int current = end; - unsigned int previous = last; - while (current != begin) { - // Calculate the size of the current dive. - unsigned int size = RB_PROFILE_DISTANCE (previous, current); - message ("Pointers: dive=%u, current=%04x, previous=%04x, size=%u, remaining=%u, available=%u\n", - ndives + 1, current, previous, size, remaining, available); - - assert (size >= 4 && size <= remaining); - - unsigned int nbytes = available; - unsigned int address = current - available; - while (nbytes < size) { - // Calculate the package size. Try with the largest possible - // size first, and adjust when the end of the ringbuffer or - // the end of the profile data is reached. - unsigned int len = SUUNTO_D9_PACKET_SIZE; - if (RB_PROFILE_BEGIN + len > address) - len = address - RB_PROFILE_BEGIN; // End of ringbuffer. - if (nbytes + len > remaining) - len = remaining - nbytes; // End of profile. - /*if (nbytes + len > size) - len = size - nbytes;*/ // End of dive (for testing only). - - message ("Pointers: address=%04x, len=%u\n", address - len, len); - - // Always read at least the minimum amount of bytes, because - // reading fewer bytes is unreliable. The memory buffer is - // large enough to prevent buffer overflows, and the extra - // bytes are automatically ignored (due to reading backwards). - unsigned int extra = 0; - if (len < MINIMUM) - extra = MINIMUM - len; - - message ("Pointers: extra=%u\n", extra); - - // Read the package. - unsigned char *p = data + MINIMUM + remaining - nbytes; - rc = suunto_d9_read (abstract, address - (len + extra), p - (len + extra), len + extra, NULL); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory."); - return rc; - } - - // Update and emit a progress event. - progress.current += len; - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Next package. - nbytes += len; - address -= len; - if (address <= RB_PROFILE_BEGIN) - address = RB_PROFILE_END; - } - - message ("Pointers: nbytes=%u\n", nbytes); - - // The last package of the current dive contains the previous and - // next pointers (in a continuous memory area). It can also contain - // a number of bytes from the next dive. The offset to the pointers - // is equal to the number of bytes remaining after the current dive. - - remaining -= size; - available = nbytes - size; - - unsigned int oprevious = array_uint16_le (data + MINIMUM + remaining + 0); - unsigned int onext = array_uint16_le (data + MINIMUM + remaining + 2); - message ("Pointers: previous=%04x, next=%04x\n", oprevious, onext); - assert (current == onext); - - // Next dive. - current = previous; - previous = oprevious; - ndives++; - -#ifndef NDEBUG - message ("D9Profile()=\""); - for (unsigned int i = 0; i < size - 4; ++i) { - message ("%02x", data[MINIMUM + remaining + 4 + i]); - } - message ("\"\n"); -#endif - - unsigned int offset = MINIMUM + remaining; - if (memcmp (data + offset + FP_OFFSET, device->fingerprint, FP_SIZE) == 0) - return DEVICE_STATUS_SUCCESS; - - if (callback && !callback (data + offset + 4, size - 4, userdata)) - return DEVICE_STATUS_SUCCESS; - } - assert (remaining == 0); - assert (available == 0); - - return DEVICE_STATUS_SUCCESS; + return suunto_common2_device_reset_maxdepth (abstract); } diff --git a/src/suunto_vyper2.c b/src/suunto_vyper2.c index d4010c5..956e727 100644 --- a/src/suunto_vyper2.c +++ b/src/suunto_vyper2.c @@ -23,19 +23,13 @@ #include // malloc, free #include // assert -#include "device-private.h" +#include "suunto_common2.h" #include "suunto_vyper2.h" #include "serial.h" #include "utils.h" -#include "ringbuffer.h" #include "checksum.h" #include "array.h" -#define MAXRETRIES 2 - -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#define MAX(a,b) (((a) > (b)) ? (a) : (b)) - #define WARNING(expr) \ { \ message ("%s:%d: %s\n", __FILE__, __LINE__, expr); \ @@ -46,38 +40,26 @@ rc == -1 ? DEVICE_STATUS_IO : DEVICE_STATUS_TIMEOUT \ ) -#define MINIMUM 8 - -#define RB_PROFILE_BEGIN 0x019A -#define RB_PROFILE_END SUUNTO_VYPER2_MEMORY_SIZE - 2 -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, RB_PROFILE_BEGIN, RB_PROFILE_END) - -#define FP_OFFSET 0x15 -#define FP_SIZE 7 - typedef struct suunto_vyper2_device_t { - device_t base; + suunto_common2_device_t base; struct serial *port; - unsigned char fingerprint[FP_SIZE]; } suunto_vyper2_device_t; -static device_status_t suunto_vyper2_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size); -static device_status_t suunto_vyper2_device_version (device_t *abstract, unsigned char data[], unsigned int size); -static device_status_t suunto_vyper2_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); -static device_status_t suunto_vyper2_device_write (device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size); -static device_status_t suunto_vyper2_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result); -static device_status_t suunto_vyper2_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata); +static device_status_t suunto_vyper2_device_packet (device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size); static device_status_t suunto_vyper2_device_close (device_t *abstract); -static const device_backend_t suunto_vyper2_device_backend = { - DEVICE_TYPE_SUUNTO_VYPER2, - suunto_vyper2_device_set_fingerprint, /* set_fingerprint */ - suunto_vyper2_device_version, /* version */ - suunto_vyper2_device_read, /* read */ - suunto_vyper2_device_write, /* write */ - suunto_vyper2_device_dump, /* dump */ - suunto_vyper2_device_foreach, /* foreach */ - suunto_vyper2_device_close /* close */ +static const suunto_common2_device_backend_t suunto_vyper2_device_backend = { + { + DEVICE_TYPE_SUUNTO_VYPER2, + suunto_common2_device_set_fingerprint, /* set_fingerprint */ + suunto_common2_device_version, /* version */ + suunto_common2_device_read, /* read */ + suunto_common2_device_write, /* write */ + suunto_common2_device_dump, /* dump */ + suunto_common2_device_foreach, /* foreach */ + suunto_vyper2_device_close /* close */ + }, + suunto_vyper2_device_packet }; static int @@ -86,7 +68,7 @@ device_is_suunto_vyper2 (device_t *abstract) if (abstract == NULL) return 0; - return abstract->backend == &suunto_vyper2_device_backend; + return abstract->backend == (const device_backend_t *) &suunto_vyper2_device_backend; } @@ -104,11 +86,10 @@ suunto_vyper2_device_open (device_t **out, const char* name) } // Initialize the base class. - device_init (&device->base, &suunto_vyper2_device_backend); + suunto_common2_device_init (&device->base, &suunto_vyper2_device_backend); // Set the default values. device->port = NULL; - memset (device->fingerprint, 0, FP_SIZE); // Open the device. int rc = serial_open (&device->port, name); @@ -177,8 +158,10 @@ suunto_vyper2_device_close (device_t *abstract) static device_status_t -suunto_vyper2_send (suunto_vyper2_device_t *device, const unsigned char command[], unsigned int csize) +suunto_vyper2_device_packet (device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) { + suunto_vyper2_device_t *device = (suunto_vyper2_device_t *) abstract; + serial_sleep (0x190 + 0xC8); // Set RTS to send the command. @@ -194,20 +177,6 @@ suunto_vyper2_send (suunto_vyper2_device_t *device, const unsigned char command[ // Clear RTS to receive the reply. serial_set_rts (device->port, 0); - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_vyper2_packet (suunto_vyper2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) -{ - // Send the command to the dive computer. - device_status_t rc = suunto_vyper2_send (device, command, csize); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Failed to send the command."); - return rc; - } - // Receive the answer of the dive computer. int n = serial_read (device->port, answer, asize); if (n != asize) { @@ -245,417 +214,11 @@ suunto_vyper2_packet (suunto_vyper2_device_t *device, const unsigned char comman } -static device_status_t -suunto_vyper2_transfer (suunto_vyper2_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size) -{ - assert (asize >= size + 4); - - // Occasionally, the dive computer does not respond to a command. - // In that case we retry the command a number of times before - // returning an error. Usually the dive computer will respond - // again during one of the retries. - - unsigned int nretries = 0; - device_status_t rc = DEVICE_STATUS_SUCCESS; - while ((rc = suunto_vyper2_packet (device, command, csize, answer, asize, size)) != DEVICE_STATUS_SUCCESS) { - // Automatically discard a corrupted packet, - // and request a new one. - if (rc != DEVICE_STATUS_TIMEOUT && rc != DEVICE_STATUS_PROTOCOL) - return rc; - - // Abort if the maximum number of retries is reached. - if (nretries++ >= MAXRETRIES) - return rc; - } - - return rc; -} - - -static device_status_t -suunto_vyper2_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) -{ - suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; - - if (! device_is_suunto_vyper2 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - if (size && size != FP_SIZE) - return DEVICE_STATUS_ERROR; - - if (size) - memcpy (device->fingerprint, data, FP_SIZE); - else - memset (device->fingerprint, 0, FP_SIZE); - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_vyper2_device_version (device_t *abstract, unsigned char data[], unsigned int size) -{ - suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; - - if (! device_is_suunto_vyper2 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - if (size < SUUNTO_VYPER2_VERSION_SIZE) { - WARNING ("Insufficient buffer space available."); - return DEVICE_STATUS_MEMORY; - } - - unsigned char answer[SUUNTO_VYPER2_VERSION_SIZE + 4] = {0}; - unsigned char command[4] = {0x0F, 0x00, 0x00, 0x0F}; - device_status_t rc = suunto_vyper2_transfer (device, command, sizeof (command), answer, sizeof (answer), 4); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - - memcpy (data, answer + 3, SUUNTO_VYPER2_VERSION_SIZE); - -#ifndef NDEBUG - message ("Vyper2ReadVersion()=\"%02x %02x %02x %02x\"\n", data[0], data[1], data[2], data[3]); -#endif - - return DEVICE_STATUS_SUCCESS; -} - - device_status_t suunto_vyper2_device_reset_maxdepth (device_t *abstract) { - suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; - if (! device_is_suunto_vyper2 (abstract)) return DEVICE_STATUS_TYPE_MISMATCH; - unsigned char answer[4] = {0}; - unsigned char command[4] = {0x20, 0x00, 0x00, 0x20}; - device_status_t rc = suunto_vyper2_transfer (device, command, sizeof (command), answer, sizeof (answer), 0); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - -#ifndef NDEBUG - message ("Vyper2ResetMaxDepth()\n"); -#endif - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_vyper2_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size, device_progress_t *progress) -{ - suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; - - if (! device_is_suunto_vyper2 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - // The data transmission is split in packages - // of maximum $SUUNTO_VYPER2_PACKET_SIZE bytes. - - unsigned int nbytes = 0; - while (nbytes < size) { - // Calculate the package size. - unsigned int len = MIN (size - nbytes, SUUNTO_VYPER2_PACKET_SIZE); - - // Read the package. - unsigned char answer[SUUNTO_VYPER2_PACKET_SIZE + 7] = {0}; - unsigned char command[7] = {0x05, 0x00, 0x03, - (address >> 8) & 0xFF, // high - (address ) & 0xFF, // low - len, // count - 0}; // CRC - command[6] = checksum_xor_uint8 (command, 6, 0x00); - device_status_t rc = suunto_vyper2_transfer (device, command, sizeof (command), answer, len + 7, len); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - - memcpy (data, answer + 6, len); - -#ifndef NDEBUG - message ("Vyper2Read(0x%04x,%d)=\"", address, len); - for (unsigned int i = 0; i < len; ++i) { - message("%02x", data[i]); - } - message("\"\n"); -#endif - - // Update and emit a progress event. - if (progress) { - progress->current += len; - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, progress); - } - - nbytes += len; - address += len; - data += len; - } - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_vyper2_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) -{ - return suunto_vyper2_read (abstract, address, data, size, NULL); -} - - -static device_status_t -suunto_vyper2_device_write (device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size) -{ - suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; - - if (! device_is_suunto_vyper2 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - // The data transmission is split in packages - // of maximum $SUUNTO_VYPER2_PACKET_SIZE bytes. - - unsigned int nbytes = 0; - while (nbytes < size) { - // Calculate the package size. - unsigned int len = MIN (size - nbytes, SUUNTO_VYPER2_PACKET_SIZE); - - // Write the package. - unsigned char answer[7] = {0}; - unsigned char command[SUUNTO_VYPER2_PACKET_SIZE + 7] = {0x06, 0x00, len + 3, - (address >> 8) & 0xFF, // high - (address ) & 0xFF, // low - len, // count - 0}; // data + CRC - memcpy (command + 6, data, len); - command[len + 6] = checksum_xor_uint8 (command, len + 6, 0x00); - device_status_t rc = suunto_vyper2_transfer (device, command, len + 7, answer, sizeof (answer), 0); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - -#ifndef NDEBUG - message ("Vyper2Write(0x%04x,%d,\"", address, len); - for (unsigned int i = 0; i < len; ++i) { - message ("%02x", data[i]); - } - message ("\");\n"); -#endif - - nbytes += len; - address += len; - data += len; - } - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_vyper2_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result) -{ - if (! device_is_suunto_vyper2 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - if (size < SUUNTO_VYPER2_MEMORY_SIZE) { - WARNING ("Insufficient buffer space available."); - return DEVICE_STATUS_MEMORY; - } - - // Enable progress notifications. - device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; - progress.maximum = SUUNTO_VYPER2_MEMORY_SIZE; - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - device_status_t rc = suunto_vyper2_read (abstract, 0x00, data, SUUNTO_VYPER2_MEMORY_SIZE, &progress); - if (rc != DEVICE_STATUS_SUCCESS) - return rc; - - if (result) - *result = SUUNTO_VYPER2_MEMORY_SIZE; - - return DEVICE_STATUS_SUCCESS; -} - - -static device_status_t -suunto_vyper2_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) -{ - suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract; - - if (! device_is_suunto_vyper2 (abstract)) - return DEVICE_STATUS_TYPE_MISMATCH; - - // Enable progress notifications. - device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; - progress.maximum = RB_PROFILE_END - RB_PROFILE_BEGIN + 8 + 4 + (MINIMUM > 4 ? MINIMUM : 4); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Read the version info. - unsigned char version[4] = {0}; - device_status_t rc = suunto_vyper2_device_version (abstract, version, sizeof (version)); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory header."); - return rc; - } - - // Update and emit a progress event. - progress.current += sizeof (version); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Read the serial number. - unsigned char serial[MINIMUM > 4 ? MINIMUM : 4] = {0}; - rc = suunto_vyper2_read (abstract, 0x0023, serial, sizeof (serial), NULL); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory header."); - return rc; - } - - // Update and emit a progress event. - progress.current += sizeof (serial); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Emit a device info event. - device_devinfo_t devinfo; - devinfo.model = version[0]; - devinfo.firmware = array_uint24_be (version + 1); - devinfo.serial = array_uint32_be (serial); - device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo); - - // Read the header bytes. - unsigned char header[8] = {0}; - rc = suunto_vyper2_read (abstract, 0x0190, header, sizeof (header), NULL); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory header."); - return rc; - } - - // Obtain the pointers from the header. - unsigned int last = array_uint16_le (header + 0); - unsigned int count = array_uint16_le (header + 2); - unsigned int end = array_uint16_le (header + 4); - unsigned int begin = array_uint16_le (header + 6); - message ("Pointers: begin=%04x, last=%04x, end=%04x, count=%i\n", begin, last, end, count); - - // Memory buffer to store all the dives. - - unsigned char data[MINIMUM + RB_PROFILE_END - RB_PROFILE_BEGIN] = {0}; - - // Calculate the total amount of bytes. - - unsigned int remaining = RB_PROFILE_DISTANCE (begin, end); - - // Update and emit a progress event. - - progress.maximum -= (RB_PROFILE_END - RB_PROFILE_BEGIN) - remaining; - progress.current += sizeof (header); - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // To reduce the number of read operations, we always try to read - // packages with the largest possible size. As a consequence, the - // last package of a dive can contain data from more than one dive. - // Therefore, the remaining data of this package (and its size) - // needs to be preserved for the next dive. - - unsigned int available = 0; - - // The ring buffer is traversed backwards to retrieve the most recent - // dives first. This allows you to download only the new dives. During - // the traversal, the current pointer does always point to the end of - // the dive data and we move to the "next" dive by means of the previous - // pointer. - - unsigned int ndives = 0; - unsigned int current = end; - unsigned int previous = last; - while (current != begin) { - // Calculate the size of the current dive. - unsigned int size = RB_PROFILE_DISTANCE (previous, current); - message ("Pointers: dive=%u, current=%04x, previous=%04x, size=%u, remaining=%u, available=%u\n", - ndives + 1, current, previous, size, remaining, available); - - assert (size >= 4 && size <= remaining); - - unsigned int nbytes = available; - unsigned int address = current - available; - while (nbytes < size) { - // Calculate the package size. Try with the largest possible - // size first, and adjust when the end of the ringbuffer or - // the end of the profile data is reached. - unsigned int len = SUUNTO_VYPER2_PACKET_SIZE; - if (RB_PROFILE_BEGIN + len > address) - len = address - RB_PROFILE_BEGIN; // End of ringbuffer. - if (nbytes + len > remaining) - len = remaining - nbytes; // End of profile. - /*if (nbytes + len > size) - len = size - nbytes;*/ // End of dive (for testing only). - - message ("Pointers: address=%04x, len=%u\n", address - len, len); - - // Always read at least the minimum amount of bytes, because - // reading fewer bytes is unreliable. The memory buffer is - // large enough to prevent buffer overflows, and the extra - // bytes are automatically ignored (due to reading backwards). - unsigned int extra = 0; - if (len < MINIMUM) - extra = MINIMUM - len; - - message ("Pointers: extra=%u\n", extra); - - // Read the package. - unsigned char *p = data + MINIMUM + remaining - nbytes; - rc = suunto_vyper2_read (abstract, address - (len + extra), p - (len + extra), len + extra, NULL); - if (rc != DEVICE_STATUS_SUCCESS) { - WARNING ("Cannot read memory."); - return rc; - } - - // Update and emit a progress event. - progress.current += len; - device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); - - // Next package. - nbytes += len; - address -= len; - if (address <= RB_PROFILE_BEGIN) - address = RB_PROFILE_END; - } - - message ("Pointers: nbytes=%u\n", nbytes); - - // The last package of the current dive contains the previous and - // next pointers (in a continuous memory area). It can also contain - // a number of bytes from the next dive. The offset to the pointers - // is equal to the number of bytes remaining after the current dive. - - remaining -= size; - available = nbytes - size; - - unsigned int oprevious = array_uint16_le (data + MINIMUM + remaining + 0); - unsigned int onext = array_uint16_le (data + MINIMUM + remaining + 2); - message ("Pointers: previous=%04x, next=%04x\n", oprevious, onext); - assert (current == onext); - - // Next dive. - current = previous; - previous = oprevious; - ndives++; - -#ifndef NDEBUG - message ("Vyper2Profile()=\""); - for (unsigned int i = 0; i < size - 4; ++i) { - message ("%02x", data[MINIMUM + remaining + 4 + i]); - } - message ("\"\n"); -#endif - - unsigned int offset = MINIMUM + remaining; - if (memcmp (data + offset + FP_OFFSET, device->fingerprint, FP_SIZE) == 0) - return DEVICE_STATUS_SUCCESS; - - if (callback && !callback (data + offset + 4, size - 4, userdata)) - return DEVICE_STATUS_SUCCESS; - } - assert (remaining == 0); - assert (available == 0); - - return DEVICE_STATUS_SUCCESS; + return suunto_common2_device_reset_maxdepth (abstract); }