diff --git a/examples/universal.c b/examples/universal.c index 332b0bf..b83407f 100644 --- a/examples/universal.c +++ b/examples/universal.c @@ -90,6 +90,7 @@ static const backend_table_t g_backends[] = { {"atom2", DEVICE_TYPE_OCEANIC_ATOM2}, {"nemo", DEVICE_TYPE_MARES_NEMO}, {"puck", DEVICE_TYPE_MARES_PUCK}, + {"darwinair", DEVICE_TYPE_MARES_DARWINAIR}, {"iconhd", DEVICE_TYPE_MARES_ICONHD}, {"ostc", DEVICE_TYPE_HW_OSTC}, {"edy", DEVICE_TYPE_CRESSI_EDY}, @@ -334,6 +335,9 @@ doparse (FILE *fp, device_data_t *devdata, const unsigned char data[], unsigned case DEVICE_TYPE_MARES_PUCK: rc = mares_nemo_parser_create (&parser, devdata->devinfo.model); break; + case DEVICE_TYPE_MARES_DARWINAIR: + rc = mares_darwinair_parser_create (&parser); + break; case DEVICE_TYPE_MARES_ICONHD: rc = mares_iconhd_parser_create (&parser, devdata->devinfo.model); break; @@ -630,6 +634,9 @@ dowork (device_type_t backend, const char *devname, const char *rawfile, const c case DEVICE_TYPE_MARES_PUCK: rc = mares_puck_device_open (&device, devname); break; + case DEVICE_TYPE_MARES_DARWINAIR: + rc = mares_darwinair_device_open (&device, devname); + break; case DEVICE_TYPE_MARES_ICONHD: rc = mares_iconhd_device_open (&device, devname); break; diff --git a/src/Makefile.am b/src/Makefile.am index 9a115e0..a2991c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,7 @@ libdivecomputer_HEADERS = \ mares.h \ mares_nemo.h \ mares_puck.h \ + mares_darwinair.h \ mares_iconhd.h \ hw.h \ hw_ostc.h \ @@ -86,6 +87,7 @@ libdivecomputer_la_SOURCES = \ mares_common.h mares_common.c \ mares_nemo.h mares_nemo.c mares_nemo_parser.c \ mares_puck.h mares_puck.c \ + mares_darwinair.h mares_darwinair.c mares_darwinair_parser.c \ mares_iconhd.h mares_iconhd.c mares_iconhd_parser.c \ hw.h \ hw_ostc.h hw_ostc.c hw_ostc_parser.c \ diff --git a/src/device.h b/src/device.h index bc7c4aa..37d0417 100644 --- a/src/device.h +++ b/src/device.h @@ -47,6 +47,7 @@ typedef enum device_type_t { DEVICE_TYPE_OCEANIC_VTPRO, DEVICE_TYPE_MARES_NEMO, DEVICE_TYPE_MARES_PUCK, + DEVICE_TYPE_MARES_DARWINAIR, DEVICE_TYPE_MARES_ICONHD, DEVICE_TYPE_HW_OSTC, DEVICE_TYPE_CRESSI_EDY, diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 3283d5b..60cc997 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -36,6 +36,7 @@ suunto_solution_parser_create suunto_eon_parser_create suunto_d9_parser_create mares_nemo_parser_create +mares_darwinair_parser_create mares_iconhd_parser_create oceanic_vtpro_parser_create oceanic_veo250_parser_create @@ -64,6 +65,8 @@ mares_nemo_device_open mares_nemo_extract_dives mares_puck_device_open mares_puck_extract_dives +mares_darwinair_device_open +mares_darwinair_extract_dives mares_iconhd_device_open mares_iconhd_extract_dives oceanic_atom2_device_open diff --git a/src/mares.h b/src/mares.h index 0dcaa2e..544a10a 100644 --- a/src/mares.h +++ b/src/mares.h @@ -24,6 +24,7 @@ #include "mares_nemo.h" #include "mares_puck.h" +#include "mares_darwinair.h" #include "mares_iconhd.h" #endif /* MARES_H */ diff --git a/src/mares_common.c b/src/mares_common.c index f0333c3..85800cb 100644 --- a/src/mares_common.c +++ b/src/mares_common.c @@ -48,6 +48,7 @@ mares_common_device_init (mares_common_device_t *device, const device_backend_t // Set the default values. device->port = NULL; + device->echo = 0; } @@ -134,6 +135,22 @@ mares_common_packet (mares_common_device_t *device, const unsigned char command[ return EXITCODE (n); } + if (device->echo) { + // Receive the echo of the command. + unsigned char echo[PACKETSIZE] = {0}; + n = serial_read (device->port, echo, csize); + if (n != csize) { + WARNING ("Failed to receive the echo."); + return EXITCODE (n); + } + + // Verify the echo. + if (memcmp (echo, command, csize) != 0) { + WARNING ("Unexpected echo."); + return DEVICE_STATUS_PROTOCOL; + } + } + // Receive the answer of the device. n = serial_read (device->port, answer, asize); if (n != asize) { diff --git a/src/mares_common.h b/src/mares_common.h index 74b4298..3b312ac 100644 --- a/src/mares_common.h +++ b/src/mares_common.h @@ -42,6 +42,7 @@ typedef struct mares_common_layout_t { typedef struct mares_common_device_t { device_t base; serial_t *port; + unsigned int echo; } mares_common_device_t; void diff --git a/src/mares_darwinair.c b/src/mares_darwinair.c new file mode 100644 index 0000000..55129e5 --- /dev/null +++ b/src/mares_darwinair.c @@ -0,0 +1,286 @@ +/* + * libdivecomputer + * + * Copyright (C) 2011 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 +#include +#include + +#include "device-private.h" +#include "mares_common.h" +#include "mares_darwinair.h" +#include "units.h" +#include "utils.h" +#include "array.h" + +#define MEMORYSIZE 0x4000 + +#define RB_LOGBOOK_OFFSET 0x0100 +#define RB_LOGBOOK_SIZE 60 +#define RB_LOGBOOK_COUNT 50 + +#define RB_PROFILE_BEGIN 0x0CC0 +#define RB_PROFILE_END 0x3FFF + +typedef struct mares_darwinair_device_t { + mares_common_device_t base; + unsigned char fingerprint[6]; +} mares_darwinair_device_t; + +static device_status_t mares_darwinair_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size); +static device_status_t mares_darwinair_device_dump (device_t *abstract, dc_buffer_t *buffer); +static device_status_t mares_darwinair_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata); +static device_status_t mares_darwinair_device_close (device_t *abstract); + +static const device_backend_t mares_darwinair_device_backend = { + DEVICE_TYPE_MARES_DARWINAIR, + mares_darwinair_device_set_fingerprint, /* set_fingerprint */ + NULL, /* version */ + mares_common_device_read, /* read */ + NULL, /* write */ + mares_darwinair_device_dump, /* dump */ + mares_darwinair_device_foreach, /* foreach */ + mares_darwinair_device_close /* close */ +}; + +static int +device_is_mares_darwinair (device_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &mares_darwinair_device_backend; +} + +device_status_t +mares_darwinair_device_open (device_t **out, const char* name) +{ + if (out == NULL) + return DEVICE_STATUS_ERROR; + + // Allocate memory. + mares_darwinair_device_t *device = (mares_darwinair_device_t *) malloc (sizeof (mares_darwinair_device_t)); + if (device == NULL) { + WARNING ("Failed to allocate memory."); + return DEVICE_STATUS_MEMORY; + } + + // Initialize the base class. + mares_common_device_init (&device->base, &mares_darwinair_device_backend); + + // Set the default values. + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + // Open the device. + int rc = serial_open (&device->base.port, name); + if (rc == -1) { + WARNING ("Failed to open the serial port."); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the serial communication protocol (9600 8N1). + rc = serial_configure (device->base.port, 9600, 8, SERIAL_PARITY_NONE, 1, SERIAL_FLOWCONTROL_NONE); + if (rc == -1) { + WARNING ("Failed to set the terminal attributes."); + serial_close (device->base.port); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the timeout for receiving data (1000 ms). + if (serial_set_timeout (device->base.port, 1000) == -1) { + WARNING ("Failed to set the timeout."); + serial_close (device->base.port); + free (device); + return DEVICE_STATUS_IO; + } + + // Set the DTR/RTS lines. + if (serial_set_dtr (device->base.port, 1) == -1 || + serial_set_rts (device->base.port, 1) == -1) { + WARNING ("Failed to set the DTR/RTS line."); + serial_close (device->base.port); + free (device); + return DEVICE_STATUS_IO; + } + + // Make sure everything is in a sane state. + serial_flush (device->base.port, SERIAL_QUEUE_BOTH); + + // Override the base class values. + device->base.echo = 1; + + *out = (device_t *) device; + + return DEVICE_STATUS_SUCCESS; +} + +static device_status_t +mares_darwinair_device_close (device_t *abstract) +{ + mares_darwinair_device_t *device = (mares_darwinair_device_t *) abstract; + + // Close the device. + if (serial_close (device->base.port) == -1) { + free (device); + return DEVICE_STATUS_IO; + } + + // Free memory. + free (device); + + return DEVICE_STATUS_SUCCESS; +} + + +static device_status_t +mares_darwinair_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) +{ + mares_darwinair_device_t *device = (mares_darwinair_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 +mares_darwinair_device_dump (device_t *abstract, dc_buffer_t *buffer) +{ + // Erase the current contents of the buffer and + // allocate the required amount of memory. + if (!dc_buffer_clear (buffer) || !dc_buffer_resize (buffer, MEMORYSIZE)) { + WARNING ("Insufficient buffer space available."); + return DEVICE_STATUS_MEMORY; + } + + return device_dump_read (abstract, dc_buffer_get_data (buffer), + dc_buffer_get_size (buffer), PACKETSIZE); +} + + +static device_status_t +mares_darwinair_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) +{ + dc_buffer_t *buffer = dc_buffer_new (MEMORYSIZE); + if (buffer == NULL) + return DEVICE_STATUS_MEMORY; + + device_status_t rc = mares_darwinair_device_dump (abstract, buffer); + if (rc != DEVICE_STATUS_SUCCESS) { + dc_buffer_free (buffer); + return rc; + } + + rc = mares_darwinair_extract_dives (abstract, dc_buffer_get_data (buffer), + dc_buffer_get_size (buffer), callback, userdata); + + dc_buffer_free (buffer); + + return rc; +} + + +device_status_t +mares_darwinair_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata) +{ + mares_darwinair_device_t *device = (mares_darwinair_device_t *) abstract; + + if (abstract && !device_is_mares_darwinair (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + // Get the profile pointer. + unsigned int eop = array_uint16_be (data + 0x8A); + if (eop < RB_PROFILE_BEGIN || eop >= RB_PROFILE_END) { + WARNING ("Invalid ringbuffer pointer detected."); + return DEVICE_STATUS_ERROR; + } + + // Get the logbook index. + unsigned int last = data[0x8C]; + if (last >= RB_LOGBOOK_COUNT) { + WARNING ("Invalid ringbuffer pointer detected."); + return DEVICE_STATUS_ERROR; + } + + // Allocate memory for the largest possible dive. + unsigned char *buffer = malloc (RB_LOGBOOK_SIZE + RB_PROFILE_END - RB_PROFILE_BEGIN); + if (buffer == NULL) { + WARNING ("Failed to allocate memory."); + return DEVICE_STATUS_MEMORY; + } + + // The logbook ringbuffer can store a fixed amount of entries, but there + // is no guarantee that the profile ringbuffer will contain a profile for + // each entry. The number of remaining bytes (which is initialized to the + // largest possible value) is used to detect the last valid profile. + unsigned int remaining = RB_PROFILE_END - RB_PROFILE_BEGIN; + + unsigned int current = eop; + for (unsigned int i = 0; i < RB_LOGBOOK_COUNT; ++i) { + // Get the offset to the current logbook entry in the ringbuffer. + unsigned int idx = (RB_LOGBOOK_COUNT + last - i) % RB_LOGBOOK_COUNT; + unsigned int offset = RB_LOGBOOK_OFFSET + idx * RB_LOGBOOK_SIZE; + + // Get the length of the current dive. + unsigned int nsamples = array_uint16_be (data + offset + 6); + unsigned int length = nsamples * 3; + if (nsamples == 0xFFFF || length > remaining) + break; + + // Copy the logbook entry. + memcpy (buffer, data + offset, RB_LOGBOOK_SIZE); + + // Copy the profile data. + if (current < RB_PROFILE_BEGIN + length) { + unsigned int a = current - RB_PROFILE_BEGIN; + unsigned int b = length - a; + memcpy (buffer + RB_LOGBOOK_SIZE, data + RB_PROFILE_END - b, b); + memcpy (buffer + RB_LOGBOOK_SIZE + b, data + RB_PROFILE_BEGIN, a); + current = RB_PROFILE_END - b; + } else { + memcpy (buffer + RB_LOGBOOK_SIZE, data + current - length, length); + current -= length; + } + + if (device && memcmp (buffer, device->fingerprint, sizeof (device->fingerprint)) == 0) { + free (buffer); + return DEVICE_STATUS_SUCCESS; + } + + if (callback && !callback (buffer, RB_LOGBOOK_SIZE + length, buffer, 6, userdata)) { + free (buffer); + return DEVICE_STATUS_SUCCESS; + } + + remaining -= length; + } + + free (buffer); + + return DEVICE_STATUS_SUCCESS; +} diff --git a/src/mares_darwinair.h b/src/mares_darwinair.h new file mode 100644 index 0000000..dba8d41 --- /dev/null +++ b/src/mares_darwinair.h @@ -0,0 +1,44 @@ +/* + * libdivecomputer + * + * Copyright (C) 2011 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 MARES_DARWINAIR_H +#define MARES_DARWINAIR_H + +#include "device.h" +#include "parser.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +device_status_t +mares_darwinair_device_open (device_t **device, const char *name); + +device_status_t +mares_darwinair_extract_dives (device_t *device, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata); + +parser_status_t +mares_darwinair_parser_create (parser_t **parser); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* MARES_DARWINAIR_H */ diff --git a/src/mares_darwinair_parser.c b/src/mares_darwinair_parser.c new file mode 100644 index 0000000..7708f84 --- /dev/null +++ b/src/mares_darwinair_parser.c @@ -0,0 +1,189 @@ +/* + * libdivecomputer + * + * Copyright (C) 2011 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 +#include + +#include "mares_darwinair.h" +#include "parser-private.h" +#include "units.h" +#include "utils.h" +#include "array.h" + +#define HEADERSIZE 60 + +typedef struct mares_darwinair_parser_t mares_darwinair_parser_t; + +struct mares_darwinair_parser_t { + parser_t base; +}; + +static parser_status_t mares_darwinair_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size); +static parser_status_t mares_darwinair_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime); +static parser_status_t mares_darwinair_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value); +static parser_status_t mares_darwinair_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata); +static parser_status_t mares_darwinair_parser_destroy (parser_t *abstract); + +static const parser_backend_t mares_darwinair_parser_backend = { + PARSER_TYPE_MARES_DARWINAIR, + mares_darwinair_parser_set_data, /* set_data */ + mares_darwinair_parser_get_datetime, /* datetime */ + mares_darwinair_parser_get_field, /* fields */ + mares_darwinair_parser_samples_foreach, /* samples_foreach */ + mares_darwinair_parser_destroy /* destroy */ +}; + + +static int +parser_is_mares_darwinair (parser_t *abstract) +{ + if (abstract == NULL) + return 0; + + return abstract->backend == &mares_darwinair_parser_backend; +} + + +parser_status_t +mares_darwinair_parser_create (parser_t **out) +{ + if (out == NULL) + return PARSER_STATUS_ERROR; + + // Allocate memory. + mares_darwinair_parser_t *parser = (mares_darwinair_parser_t *) malloc (sizeof (mares_darwinair_parser_t)); + if (parser == NULL) { + WARNING ("Failed to allocate memory."); + return PARSER_STATUS_MEMORY; + } + + // Initialize the base class. + parser_init (&parser->base, &mares_darwinair_parser_backend); + + *out = (parser_t *) parser; + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_darwinair_parser_destroy (parser_t *abstract) +{ + if (! parser_is_mares_darwinair (abstract)) + return PARSER_STATUS_TYPE_MISMATCH; + + // Free memory. + free (abstract); + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_darwinair_parser_set_data (parser_t *abstract, const unsigned char *data, unsigned int size) +{ + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_darwinair_parser_get_datetime (parser_t *abstract, dc_datetime_t *datetime) +{ + if (abstract->size < HEADERSIZE) + return PARSER_STATUS_ERROR; + + const unsigned char *p = abstract->data; + + if (datetime) { + datetime->year = array_uint16_be (p); + datetime->month = p[2]; + datetime->day = p[3]; + datetime->hour = p[4]; + datetime->minute = p[5]; + datetime->second = 0; + } + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_darwinair_parser_get_field (parser_t *abstract, parser_field_type_t type, unsigned int flags, void *value) +{ + if (abstract->size < HEADERSIZE) + return PARSER_STATUS_ERROR; + + const unsigned char *p = abstract->data; + + gasmix_t *gasmix = (gasmix_t *) value; + + if (value) { + switch (type) { + case FIELD_TYPE_DIVETIME: + *((unsigned int *) value) = array_uint16_be (p + 0x06) * 20; + break; + case FIELD_TYPE_MAXDEPTH: + *((double *) value) = array_uint16_be (p + 0x08) / 10.0; + break; + case FIELD_TYPE_GASMIX_COUNT: + *((unsigned int *) value) = 1; + break; + case FIELD_TYPE_GASMIX: + gasmix->helium = 0.0; + gasmix->oxygen = 0.21; + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + default: + return PARSER_STATUS_UNSUPPORTED; + } + } + + return PARSER_STATUS_SUCCESS; +} + + +static parser_status_t +mares_darwinair_parser_samples_foreach (parser_t *abstract, sample_callback_t callback, void *userdata) +{ + if (abstract->size < HEADERSIZE) + return PARSER_STATUS_ERROR; + + unsigned int time = 0; + + unsigned int offset = HEADERSIZE; + while (offset + 3 <= abstract->size) { + parser_sample_value_t sample = {0}; + + // Surface Time (seconds). + time += 20; + sample.time = time; + if (callback) callback (SAMPLE_TYPE_TIME, sample, userdata); + + // Depth (1/10 m). + unsigned int depth = array_uint16_le (abstract->data + offset) & 0x07FF; + sample.depth = depth / 10.0; + if (callback) callback (SAMPLE_TYPE_DEPTH, sample, userdata); + + offset += 3; + } + + return PARSER_STATUS_SUCCESS; +} diff --git a/src/parser.h b/src/parser.h index 5f9c060..3c0656f 100644 --- a/src/parser.h +++ b/src/parser.h @@ -40,6 +40,7 @@ typedef enum parser_type_t { PARSER_TYPE_UWATEC_MEMOMOUSE, PARSER_TYPE_UWATEC_SMART, PARSER_TYPE_MARES_NEMO, + PARSER_TYPE_MARES_DARWINAIR, PARSER_TYPE_MARES_ICONHD, PARSER_TYPE_OCEANIC_VTPRO, PARSER_TYPE_OCEANIC_VEO250,