From 781f0db71f7a06591e819f7960dd5b504b4f16a0 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 22 Feb 2012 19:48:22 +0100 Subject: [PATCH] Add support for the Heinrichs Weikamp Frog. --- examples/Makefile.am | 3 + examples/hw_frog_test.c | 85 ++++++ examples/universal.c | 9 +- src/Makefile.am | 2 + src/device.h | 1 + src/hw.h | 1 + src/hw_frog.c | 542 ++++++++++++++++++++++++++++++++++++ src/hw_frog.h | 48 ++++ src/hw_ostc.h | 2 +- src/hw_ostc_parser.c | 50 +++- src/libdivecomputer.symbols | 4 + 11 files changed, 732 insertions(+), 15 deletions(-) create mode 100644 examples/hw_frog_test.c create mode 100644 src/hw_frog.c create mode 100644 src/hw_frog.h diff --git a/examples/Makefile.am b/examples/Makefile.am index 585c603..8053efc 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -21,6 +21,7 @@ bin_PROGRAMS = \ darwin \ iconhd \ ostc \ + frog \ edy \ n2ition3 @@ -68,6 +69,8 @@ iconhd_SOURCES = mares_iconhd_test.c $(COMMON) ostc_SOURCES = hw_ostc_test.c $(COMMON) +frog_SOURCES = hw_frog_test.c $(COMMON) + edy_SOURCES = cressi_edy_test.c $(COMMON) n2ition3_SOURCES = zeagle_n2ition3_test.c $(COMMON) diff --git a/examples/hw_frog_test.c b/examples/hw_frog_test.c new file mode 100644 index 0000000..d323905 --- /dev/null +++ b/examples/hw_frog_test.c @@ -0,0 +1,85 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 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 // fopen, fwrite, fclose + +#include "hw_frog.h" +#include "utils.h" + +#include "common.h" + +device_status_t +test_dump_memory (const char* name, const char* filename) +{ + device_t *device = NULL; + + message ("hw_frog_device_open\n"); + device_status_t rc = hw_frog_device_open (&device, name); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Error opening serial port."); + return rc; + } + + message ("device_foreach\n"); + rc = device_foreach (device, NULL, NULL); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot read memory."); + device_close (device); + return rc; + } + + message ("device_close\n"); + rc = device_close (device); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Cannot close device."); + return rc; + } + + return DEVICE_STATUS_SUCCESS; +} + + +int main(int argc, char *argv[]) +{ + message_set_logfile ("FROG.LOG"); + +#ifdef _WIN32 + const char* name = "COM1"; +#else + const char* name = "/dev/ttyS0"; +#endif + + if (argc > 1) { + name = argv[1]; + } + + message ("DEVICE=%s\n", name); + + device_status_t a = test_dump_memory (name, "FROG.DMP"); + + message ("SUMMARY\n"); + message ("-------\n"); + message ("test_dump_memory: %s\n", errmsg (a)); + + message_set_logfile (NULL); + + return 0; +} diff --git a/examples/universal.c b/examples/universal.c index 69cfdf0..2cfe565 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -93,6 +93,7 @@ static const backend_table_t g_backends[] = { {"darwin", DEVICE_TYPE_MARES_DARWIN}, {"iconhd", DEVICE_TYPE_MARES_ICONHD}, {"ostc", DEVICE_TYPE_HW_OSTC}, + {"frog", DEVICE_TYPE_HW_FROG}, {"edy", DEVICE_TYPE_CRESSI_EDY}, {"n2ition3", DEVICE_TYPE_ZEAGLE_N2ITION3}, {"cobalt", DEVICE_TYPE_ATOMICS_COBALT} @@ -342,7 +343,10 @@ doparse (FILE *fp, device_data_t *devdata, const unsigned char data[], unsigned rc = mares_iconhd_parser_create (&parser, devdata->devinfo.model); break; case DEVICE_TYPE_HW_OSTC: - rc = hw_ostc_parser_create (&parser); + rc = hw_ostc_parser_create (&parser, 0); + break; + case DEVICE_TYPE_HW_FROG: + rc = hw_ostc_parser_create (&parser, 1); break; case DEVICE_TYPE_CRESSI_EDY: case DEVICE_TYPE_ZEAGLE_N2ITION3: @@ -644,6 +648,9 @@ dowork (device_type_t backend, unsigned int model, const char *devname, const ch case DEVICE_TYPE_HW_OSTC: rc = hw_ostc_device_open (&device, devname); break; + case DEVICE_TYPE_HW_FROG: + rc = hw_frog_device_open (&device, devname); + break; case DEVICE_TYPE_CRESSI_EDY: rc = cressi_edy_device_open (&device, devname); break; diff --git a/src/Makefile.am b/src/Makefile.am index 81595c8..7e6440c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ libdivecomputer_HEADERS = \ mares_iconhd.h \ hw.h \ hw_ostc.h \ + hw_frog.h \ cressi.h \ cressi_edy.h \ zeagle.h \ @@ -91,6 +92,7 @@ libdivecomputer_la_SOURCES = \ mares_iconhd.h mares_iconhd.c mares_iconhd_parser.c \ hw.h \ hw_ostc.h hw_ostc.c hw_ostc_parser.c \ + hw_frog.h hw_frog.c \ cressi.h \ cressi_edy.h cressi_edy.c cressi_edy_parser.c \ zeagle.h \ diff --git a/src/device.h b/src/device.h index 90fbfb2..b4e8c78 100644 --- a/src/device.h +++ b/src/device.h @@ -50,6 +50,7 @@ typedef enum device_type_t { DEVICE_TYPE_MARES_DARWIN, DEVICE_TYPE_MARES_ICONHD, DEVICE_TYPE_HW_OSTC, + DEVICE_TYPE_HW_FROG, DEVICE_TYPE_CRESSI_EDY, DEVICE_TYPE_ZEAGLE_N2ITION3, DEVICE_TYPE_ATOMICS_COBALT diff --git a/src/hw.h b/src/hw.h index b7d6384..c16c3e1 100644 --- a/src/hw.h +++ b/src/hw.h @@ -23,5 +23,6 @@ #define HW_H #include "hw_ostc.h" +#include "hw_frog.h" #endif /* HW_H */ diff --git a/src/hw_frog.c b/src/hw_frog.c new file mode 100644 index 0000000..8e3789e --- /dev/null +++ b/src/hw_frog.c @@ -0,0 +1,542 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 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 // malloc, free + +#include "device-private.h" +#include "hw_frog.h" +#include "serial.h" +#include "checksum.h" +#include "ringbuffer.h" +#include "utils.h" +#include "array.h" + +#define EXITCODE(rc) \ +( \ + rc == -1 ? DEVICE_STATUS_IO : DEVICE_STATUS_TIMEOUT \ +) + +#define SZ_DISPLAY 15 +#define SZ_CUSTOMTEXT 13 +#define SZ_VERSION (SZ_CUSTOMTEXT + 4) + +#define RB_LOGBOOK_SIZE 256 +#define RB_LOGBOOK_COUNT 256 + +#define RB_PROFILE_BEGIN 0x000000 +#define RB_PROFILE_END 0x200000 +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) + +#define READY 0x4D +#define HEADER 0x61 +#define CLOCK 0x62 +#define CUSTOMTEXT 0x63 +#define DIVE 0x66 +#define IDENTITY 0x69 +#define DISPLAY 0x6E +#define INIT 0xBB +#define EXIT 0xFF + +typedef struct hw_frog_device_t { + device_t base; + serial_t *port; + unsigned char fingerprint[5]; +} hw_frog_device_t; + +static device_status_t hw_frog_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size); +static device_status_t hw_frog_device_version (device_t *abstract, unsigned char data[], unsigned int size); +static device_status_t hw_frog_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata); +static device_status_t hw_frog_device_close (device_t *abstract); + +static const device_backend_t hw_frog_device_backend = { + DEVICE_TYPE_HW_FROG, + hw_frog_device_set_fingerprint, /* set_fingerprint */ + hw_frog_device_version, /* version */ + NULL, /* read */ + NULL, /* write */ + NULL, /* dump */ + hw_frog_device_foreach, /* foreach */ + hw_frog_device_close /* close */ +}; + + +static int +device_is_hw_frog (device_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &hw_frog_device_backend; +} + + +static device_status_t +hw_frog_transfer (hw_frog_device_t *device, + device_progress_t *progress, + unsigned char cmd, + const unsigned char input[], + unsigned int isize, + unsigned char output[], + unsigned int osize) +{ + // Send the command. + unsigned char command[1] = {cmd}; + int n = serial_write (device->port, command, sizeof (command)); + if (n != sizeof (command)) { + WARNING ("Failed to send the command."); + return EXITCODE (n); + } + + if (cmd != INIT && cmd != HEADER) { + // Read the echo. + unsigned char answer[1] = {0}; + n = serial_read (device->port, answer, sizeof (answer)); + if (n != sizeof (answer)) { + WARNING ("Failed to receive the echo."); + return EXITCODE (n); + } + + // Verify the echo. + if (memcmp (answer, command, sizeof (command)) != 0) { + WARNING ("Unexpected echo."); + return DEVICE_STATUS_ERROR; + } + } + + if (input) { + // Send the input data packet. + n = serial_write (device->port, input, isize); + if (n != isize) { + WARNING ("Failed to send the data."); + return EXITCODE (n); + } + } + + if (output) { + unsigned int nbytes = 0; + while (nbytes < osize) { + // Set the minimum packet size. + unsigned int len = 1024; + + // Increase the packet size if more data is immediately available. + int available = serial_get_received (device->port); + if (available > len) + len = available; + + // Limit the packet size to the total size. + if (nbytes + len > osize) + len = osize - nbytes; + + // Read the packet. + n = serial_read (device->port, output + nbytes, len); + if (n != len) { + WARNING ("Failed to receive the answer."); + return EXITCODE (n); + } + + // Update and emit a progress event. + if (progress) { + progress->current += len; + device_event_emit ((device_t *) device, DEVICE_EVENT_PROGRESS, progress); + } + + nbytes += len; + } + } + + if (cmd != EXIT) { + // Read the ready byte. + unsigned char answer[1] = {0}; + n = serial_read (device->port, answer, sizeof (answer)); + if (n != sizeof (answer)) { + WARNING ("Failed to receive the ready byte."); + return EXITCODE (n); + } + + // Verify the ready byte. + if (answer[0] != READY) { + WARNING ("Unexpected ready byte."); + return DEVICE_STATUS_ERROR; + } + } + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +hw_frog_device_open (device_t **out, const char* name) +{ + if (out == NULL) + return DEVICE_STATUS_ERROR; + + // Allocate memory. + hw_frog_device_t *device = (hw_frog_device_t *) malloc (sizeof (hw_frog_device_t)); + if (device == NULL) { + WARNING ("Failed to allocate memory."); + return DEVICE_STATUS_MEMORY; + } + + // Initialize the base class. + device_init (&device->base, &hw_frog_device_backend); + + // Set the default values. + device->port = NULL; + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // Open the device. + int rc = serial_open (&device->port, name); + if (rc == -1) { + WARNING ("Failed to open the serial port."); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the serial communication protocol (115200 8N1). + rc = serial_configure (device->port, 115200, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + WARNING ("Failed to set the terminal attributes."); + serial_close (device->port); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the timeout for receiving data (3000ms). + if (serial_set_timeout (device->port, 3000) == -1) { + WARNING ("Failed to set the timeout."); + serial_close (device->port); + free (device); + return DEVICE_STATUS_IO; + } + + // Make sure everything is in a sane state. + serial_sleep (300); + serial_flush (device->port, SERIAL_QUEUE_BOTH); + + // Send the init command. + device_status_t status = hw_frog_transfer (device, NULL, INIT, NULL, 0, NULL, 0); + if (status != DEVICE_STATUS_SUCCESS) { + WARNING ("Failed to send the init command."); + serial_close (device->port); + free (device); + return status; + } + + *out = (device_t *) device; + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +hw_frog_device_close (device_t *abstract) +{ + hw_frog_device_t *device = (hw_frog_device_t*) abstract; + + // Send the exit command. + device_status_t status = hw_frog_transfer (device, NULL, EXIT, NULL, 0, NULL, 0); + if (status != DEVICE_STATUS_SUCCESS) { + WARNING ("Failed to send the exit command."); + serial_close (device->port); + free (device); + return status; + } + + // Close the device. + if (serial_close (device->port) == -1) { + free (device); + return DEVICE_STATUS_IO; + } + + // Free memory. + free (device); + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +hw_frog_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) +{ + hw_frog_device_t *device = (hw_frog_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; +} + + +static device_status_t +hw_frog_device_version (device_t *abstract, unsigned char data[], unsigned int size) +{ + hw_frog_device_t *device = (hw_frog_device_t *) abstract; + + if (!device_is_hw_frog (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + if (size != SZ_VERSION) + return DEVICE_STATUS_ERROR; + + // Send the command. + device_status_t rc = hw_frog_transfer (device, NULL, IDENTITY, NULL, 0, data, size); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +hw_frog_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) +{ + hw_frog_device_t *device = (hw_frog_device_t *) abstract; + + // Enable progress notifications. + device_progress_t progress = DEVICE_PROGRESS_INITIALIZER; + progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + + (RB_PROFILE_END - RB_PROFILE_BEGIN); + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // Download the version data. + unsigned char id[SZ_VERSION] = {0}; + device_status_t rc = hw_frog_device_version (abstract, id, sizeof (id)); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Failed to read the version."); + return rc; + } + + // Emit a device info event. + device_devinfo_t devinfo; + devinfo.model = 0; + devinfo.firmware = array_uint16_be (id + 2); + devinfo.serial = array_uint16_le (id + 0); + device_event_emit (abstract, DEVICE_EVENT_DEVINFO, &devinfo); + + // Allocate memory. + unsigned char *header = malloc (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); + if (header == NULL) { + WARNING ("Failed to allocate memory."); + return DEVICE_STATUS_MEMORY; + } + + // Download the logbook headers. + rc = hw_frog_transfer (device, &progress, HEADER, + NULL, 0, header, RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Failed to read the header."); + free (header); + return rc; + } + + // Locate the most recent dive. + // The device maintains an internal counter which is incremented for every + // dive, and the current value at the time of the dive is stored in the + // dive header. Thus the most recent dive will have the highest value. + unsigned int count = 0; + unsigned int latest = 0; + unsigned int maximum = 0; + for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { + unsigned int offset = i * RB_LOGBOOK_SIZE; + + // Ignore uninitialized header entries. + if (array_isequal (header + offset, RB_LOGBOOK_SIZE, 0xFF)) + break; + + // Get the internal dive number. + unsigned int current = array_uint16_le (header + offset + 52); + if (current > maximum) { + maximum = current; + latest = i; + } + + count++; + } + + // Calculate the total and maximum size. + unsigned int ndives = 0; + unsigned int size = 0; + unsigned int maxsize = 0; + for (unsigned int i = 0; i < count; ++i) { + unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; + unsigned int offset = idx * RB_LOGBOOK_SIZE; + + // Get the ringbuffer pointers. + unsigned int begin = array_uint24_le (header + offset + 2); + unsigned int end = array_uint24_le (header + offset + 5); + if (begin < RB_PROFILE_BEGIN || + begin >= RB_PROFILE_END || + end < RB_PROFILE_BEGIN || + end >= RB_PROFILE_END) + { + WARNING("Invalid ringbuffer pointer detected!"); + free (header); + return DEVICE_STATUS_ERROR; + } + + // Calculate the profile length. + unsigned int length = RB_LOGBOOK_SIZE + RB_PROFILE_DISTANCE (begin, end) - 6; + + // Check the fingerprint data. + if (memcmp (header + offset + 9, device->fingerprint, sizeof (device->fingerprint)) == 0) + break; + + if (length > maxsize) + maxsize = length; + size += length; + ndives++; + } + + // Update and emit a progress event. + progress.maximum = (RB_LOGBOOK_SIZE * RB_LOGBOOK_COUNT) + size; + device_event_emit (abstract, DEVICE_EVENT_PROGRESS, &progress); + + // Allocate enough memory for the largest dive. + unsigned char *profile = malloc (maxsize); + if (profile == NULL) { + WARNING ("Failed to allocate memory."); + free (header); + return DEVICE_STATUS_MEMORY; + } + + // Download the dives. + for (unsigned int i = 0; i < ndives; ++i) { + unsigned int idx = (latest + RB_LOGBOOK_COUNT - i) % RB_LOGBOOK_COUNT; + unsigned int offset = idx * RB_LOGBOOK_SIZE; + + // Get the ringbuffer pointers. + unsigned int begin = array_uint24_le (header + offset + 2); + unsigned int end = array_uint24_le (header + offset + 5); + + // Calculate the profile length. + unsigned int length = RB_LOGBOOK_SIZE + RB_PROFILE_DISTANCE (begin, end) - 6; + + // Download the dive. + unsigned char number[1] = {idx}; + rc = hw_frog_transfer (device, &progress, DIVE, + number, sizeof (number), profile, length); + if (rc != DEVICE_STATUS_SUCCESS) { + WARNING ("Failed to read the dive."); + free (profile); + free (header); + return rc; + } + + if (callback && !callback (profile, length, profile + 9, sizeof (device->fingerprint), userdata)) + break; + } + + free (profile); + free (header); + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +hw_frog_device_clock (device_t *abstract, const dc_datetime_t *datetime) +{ + hw_frog_device_t *device = (hw_frog_device_t *) abstract; + + if (!device_is_hw_frog (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + if (datetime == NULL) { + WARNING ("Invalid parameter specified."); + return DEVICE_STATUS_ERROR; + } + + // Send the command. + unsigned char packet[6] = { + datetime->hour, datetime->minute, datetime->second, + datetime->month, datetime->day, datetime->year - 2000}; + device_status_t rc = hw_frog_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +hw_frog_device_display (device_t *abstract, const char *text) +{ + hw_frog_device_t *device = (hw_frog_device_t *) abstract; + + if (!device_is_hw_frog (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + // Check the maximum length. + size_t length = (text ? strlen (text) : 0); + if (length > SZ_DISPLAY) { + WARNING ("Invalid parameter specified."); + return DEVICE_STATUS_ERROR; + } + + // Pad the data packet with spaces. + unsigned char packet[SZ_DISPLAY] = {0}; + if (length) + memcpy (packet, text, length); + memset (packet + length, 0x20, sizeof (packet) - length); + + // Send the command. + device_status_t rc = hw_frog_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + return DEVICE_STATUS_SUCCESS; +} + + +device_status_t +hw_frog_device_customtext (device_t *abstract, const char *text) +{ + hw_frog_device_t *device = (hw_frog_device_t *) abstract; + + if (!device_is_hw_frog (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + // Check the maximum length. + size_t length = (text ? strlen (text) : 0); + if (length > SZ_CUSTOMTEXT) { + WARNING ("Invalid parameter specified."); + return DEVICE_STATUS_ERROR; + } + + // Pad the data packet with spaces. + unsigned char packet[SZ_CUSTOMTEXT] = {0}; + if (length) + memcpy (packet, text, length); + memset (packet + length, 0x20, sizeof (packet) - length); + + // Send the command. + device_status_t rc = hw_frog_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + return DEVICE_STATUS_SUCCESS; +} diff --git a/src/hw_frog.h b/src/hw_frog.h new file mode 100644 index 0000000..38f0564 --- /dev/null +++ b/src/hw_frog.h @@ -0,0 +1,48 @@ +/* + * libdivecomputer + * + * Copyright (C) 2012 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 HW_FROG_H +#define HW_FROG_H + +#include "device.h" +#include "parser.h" +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +device_status_t +hw_frog_device_open (device_t **device, const char *name); + +device_status_t +hw_frog_device_clock (device_t *device, const dc_datetime_t *datetime); + +device_status_t +hw_frog_device_display (device_t *device, const char *text); + +device_status_t +hw_frog_device_customtext (device_t *abstract, const char *text); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* HW_FROG_H */ diff --git a/src/hw_ostc.h b/src/hw_ostc.h index adfc032..22214ef 100644 --- a/src/hw_ostc.h +++ b/src/hw_ostc.h @@ -61,7 +61,7 @@ device_status_t hw_ostc_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata); parser_status_t -hw_ostc_parser_create (parser_t **parser); +hw_ostc_parser_create (parser_t **parser, unsigned int frog); #ifdef __cplusplus } diff --git a/src/hw_ostc_parser.c b/src/hw_ostc_parser.c index 4ab1436..e0258fa 100644 --- a/src/hw_ostc_parser.c +++ b/src/hw_ostc_parser.c @@ -32,6 +32,7 @@ typedef struct hw_ostc_parser_t hw_ostc_parser_t; struct hw_ostc_parser_t { parser_t base; + unsigned int frog; }; typedef struct hw_ostc_sample_info_t { @@ -66,7 +67,7 @@ parser_is_hw_ostc (parser_t *abstract) parser_status_t -hw_ostc_parser_create (parser_t **out) +hw_ostc_parser_create (parser_t **out, unsigned int frog) { if (out == NULL) return PARSER_STATUS_ERROR; @@ -81,6 +82,8 @@ hw_ostc_parser_create (parser_t **out) // Initialize the base class. parser_init (&parser->base, &hw_ostc_parser_backend); + parser->frog = frog; + *out = (parser_t*) parser; return PARSER_STATUS_SUCCESS; @@ -113,14 +116,15 @@ hw_ostc_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned static parser_status_t hw_ostc_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) { + hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; - if (size < 3) + if (size < 9) return PARSER_STATUS_ERROR; // Check the profile version - unsigned int version = data[2]; + unsigned int version = data[parser->frog ? 8 : 2]; unsigned int header = 0; switch (version) { case 0x20: @@ -129,6 +133,9 @@ hw_ostc_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) case 0x21: header = 57; break; + case 0x22: + header = 256; + break; default: return PARSER_STATUS_ERROR; } @@ -137,7 +144,7 @@ hw_ostc_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) return PARSER_STATUS_ERROR; unsigned int divetime = 0; - if (version == 0x21) { + if (version == 0x21 || version == 0x22) { // Use the dive time stored in the extended header, rounded down towards // the nearest minute, to match the value displayed by the ostc. divetime = (array_uint16_le (data + 47) / 60) * 60; @@ -146,6 +153,9 @@ hw_ostc_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) divetime = array_uint16_le (data + 10) * 60 + data[12]; } + if (parser->frog) + data += 6; + dc_datetime_t dt; dt.year = data[5] + 2000; dt.month = data[3]; @@ -169,14 +179,15 @@ hw_ostc_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) static parser_status_t hw_ostc_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value) { + hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; - if (size < 3) + if (size < 9) return PARSER_STATUS_ERROR; // Check the profile version - unsigned int version = data[2]; + unsigned int version = data[parser->frog ? 8 : 2]; unsigned int header = 0; switch (version) { case 0x20: @@ -185,6 +196,9 @@ hw_ostc_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned case 0x21: header = 57; break; + case 0x22: + header = 256; + break; default: return PARSER_STATUS_ERROR; } @@ -192,6 +206,9 @@ hw_ostc_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned if (size < header) return PARSER_STATUS_ERROR; + if (parser->frog) + data += 6; + gasmix_t *gasmix = (gasmix_t *) value; if (value) { @@ -203,11 +220,17 @@ hw_ostc_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned *((double *) value) = array_uint16_le (data + 8) / 100.0; break; case FIELD_TYPE_GASMIX_COUNT: - *((unsigned int *) value) = 6; + if (parser->frog) + *((unsigned int *) value) = 3; + else + *((unsigned int *) value) = 6; break; case FIELD_TYPE_GASMIX: gasmix->oxygen = data[19 + 2 * flags] / 100.0; - gasmix->helium = data[20 + 2 * flags] / 100.0; + if (parser->frog) + gasmix->helium = 0.0; + else + gasmix->helium = data[20 + 2 * flags] / 100.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; default: @@ -222,17 +245,15 @@ hw_ostc_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned static parser_status_t hw_ostc_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) { - if (! parser_is_hw_ostc (abstract)) - return PARSER_STATUS_TYPE_MISMATCH; - + hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; const unsigned char *data = abstract->data; unsigned int size = abstract->size; - if (size < 3) + if (size < 9) return PARSER_STATUS_ERROR; // Check the profile version - unsigned int version = data[2]; + unsigned int version = data[parser->frog ? 8 : 2]; unsigned int header = 0; switch (version) { case 0x20: @@ -241,6 +262,9 @@ hw_ostc_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, case 0x21: header = 57; break; + case 0x22: + header = 256; + break; default: return PARSER_STATUS_ERROR; } diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 7ff4532..81db2e5 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -125,6 +125,10 @@ hw_ostc_device_eeprom_write hw_ostc_device_reset hw_ostc_device_screenshot hw_ostc_extract_dives +hw_frog_device_open +hw_frog_device_clock +hw_frog_device_display +hw_frog_device_customtext zeagle_n2ition3_device_open atomics_cobalt_device_open atomics_cobalt_device_set_simulation