diff --git a/src/Makefile.am b/src/Makefile.am index df450da..f80d7ae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,7 @@ libdivecomputer_la_SOURCES = \ oceanic_veo250.h oceanic_veo250.c \ oceanic_vtpro.h oceanic_vtpro.c \ mares.h \ + mares_common.h mares_common.c \ mares_nemo.h mares_nemo.c mares_nemo_parser.c \ mares_puck.h mares_puck.c \ ringbuffer.h ringbuffer.c \ diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 053819a..be6ec25 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -36,6 +36,7 @@ message_set_logfile mares_nemo_device_open mares_nemo_extract_dives mares_puck_device_open +mares_puck_extract_dives oceanic_atom2_device_open oceanic_atom2_device_keepalive oceanic_veo250_device_open diff --git a/src/mares_common.c b/src/mares_common.c new file mode 100644 index 0000000..8bd354c --- /dev/null +++ b/src/mares_common.c @@ -0,0 +1,184 @@ +/* + * 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 // malloc +#include // memcpy, memcmp +#include // assert + +#include "mares_common.h" +#include "utils.h" +#include "array.h" + +#define FP_OFFSET 8 +#define FP_SIZE 5 + +void +mares_common_device_init (mares_common_device_t *device, const device_backend_t *backend) +{ + assert (device != NULL); + + // Initialize the base class. + device_init (&device->base, backend); + + // Set the default values. + memset (device->fingerprint, 0, sizeof (device->fingerprint)); +} + + +device_status_t +mares_common_device_set_fingerprint (mares_common_device_t *device, const unsigned char data[], unsigned int size) +{ + assert (device != NULL); + + 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 +mares_common_extract_dives (mares_common_device_t *device, const mares_common_layout_t *layout, const unsigned char data[], dive_callback_t callback, void *userdata) +{ + assert (layout != NULL); + + // Get the end of the profile ring buffer. + unsigned int eop = array_uint16_le (data + 0x6B); + + // Make the ringbuffer linear, to avoid having to deal + // with the wrap point. The buffer has extra space to + // store the profile data for the freedives. + unsigned char *buffer = (unsigned char *) malloc ( + layout->rb_profile_end - layout->rb_profile_begin + + layout->rb_freedives_end - layout->rb_freedives_begin); + if (buffer == NULL) { + WARNING ("Out of memory."); + return DEVICE_STATUS_MEMORY; + } + + memcpy (buffer + 0, data + eop, layout->rb_profile_end - eop); + memcpy (buffer + layout->rb_profile_end - eop, data + layout->rb_profile_begin, eop - layout->rb_profile_begin); + + // For a freedive session, the Mares Nemo stores all the freedives of + // that session in a single logbook entry, and each sample is actually + // a summary for each individual freedive in the session. The profile + // data is stored in a separate memory area. Since only the most recent + // recent freediving session can have profile data, we keep track of the + // number of freedives. + unsigned int nfreedives = 0; + + unsigned int offset = layout->rb_profile_end - layout->rb_profile_begin; + while (offset >= 3) { + // Check the dive mode of the logbook entry. Valid modes are + // 0 (air), 1 (EANx), 2 (freedive) or 3 (bottom timer). + // If the ringbuffer has never reached the wrap point before, + // there will be "empty" memory (filled with 0xFF) and + // processing should stop at this point. + unsigned int mode = buffer[offset - 1]; + if (mode == 0xFF) + break; + + // The header and sample size are dependant on the dive mode. Only + // in freedive mode, the sizes are different from the other modes. + unsigned int header_size = 53; + unsigned int sample_size = 2; + if (mode == 2) { + header_size = 28; + sample_size = 6; + nfreedives++; + } + + // Get the number of samples in the profile data. + unsigned int nsamples = array_uint16_le (buffer + offset - 3); + + // Calculate the total number of bytes for this dive. + // If the buffer does not contain that much bytes, we reached the + // end of the ringbuffer. The current dive is incomplete (partially + // overwritten with newer data), and processing should stop. + unsigned int nbytes = 2 + nsamples * sample_size + header_size; + if (offset < nbytes) + break; + + // Move to the start of the dive. + offset -= nbytes; + + // Verify that the length that is stored in the profile data + // equals the calculated length. If both values are different, + // something is wrong and an error is returned. + unsigned int length = array_uint16_le (buffer + offset); + if (length != nbytes) { + WARNING ("Calculated and stored size are not equal."); + free (buffer); + return DEVICE_STATUS_ERROR; + } + + // Process the profile data for the most recent freedive entry. + // Since we are processing the entries backwards (newest to oldest), + // this entry will always be the first one. + if (mode == 2 && nfreedives == 1) { + // Count the number of freedives in the profile data. + unsigned int count = 0; + unsigned int idx = layout->rb_freedives_begin; + while (idx + 2 <= layout->rb_freedives_end && + count != nsamples) + { + // Each freedive in the session ends with a zero sample. + unsigned int sample = array_uint16_le (data + idx); + if (sample == 0) + count++; + + // Move to the next sample. + idx += 2; + } + + // Verify that the number of freedive entries in the session + // equals the number of freedives in the profile data. If + // both values are different, the profile data is incomplete. + assert (count == nsamples); + + // Append the profile data to the main logbook entry. The + // buffer is guaranteed to have enough space, and the dives + // that will be overwritten have already been processed. + memcpy (buffer + offset + nbytes, data + layout->rb_freedives_begin, idx - layout->rb_freedives_begin); + nbytes += idx - layout->rb_freedives_begin; + } + + unsigned int fp_offset = offset + length - FP_OFFSET; + if (device && memcmp (buffer + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) { + free (buffer); + return DEVICE_STATUS_SUCCESS; + } + + if (callback && !callback (buffer + offset, nbytes, userdata)) { + free (buffer); + return DEVICE_STATUS_SUCCESS; + } + } + + free (buffer); + + return DEVICE_STATUS_SUCCESS; +} diff --git a/src/mares_common.h b/src/mares_common.h new file mode 100644 index 0000000..a90cc6b --- /dev/null +++ b/src/mares_common.h @@ -0,0 +1,55 @@ +/* + * 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 MARES_COMMON_H +#define MARES_COMMON_H + +#include "device-private.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct mares_common_device_t { + device_t base; + unsigned char fingerprint[5]; +} mares_common_device_t; + +typedef struct mares_common_layout_t { + unsigned int rb_profile_begin; + unsigned int rb_profile_end; + unsigned int rb_freedives_begin; + unsigned int rb_freedives_end; +} mares_common_layout_t; + +void +mares_common_device_init (mares_common_device_t *device, const device_backend_t *backend); + +device_status_t +mares_common_device_set_fingerprint (mares_common_device_t *device, const unsigned char data[], unsigned int size); + +device_status_t +mares_common_extract_dives (mares_common_device_t *device, const mares_common_layout_t *layout, const unsigned char data[], dive_callback_t callback, void *userdata); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* MARES_COMMON_H */ diff --git a/src/mares_nemo.c b/src/mares_nemo.c index 554a4e1..b84f249 100644 --- a/src/mares_nemo.c +++ b/src/mares_nemo.c @@ -24,11 +24,11 @@ #include // assert #include "device-private.h" +#include "mares_common.h" #include "mares_nemo.h" #include "serial.h" #include "utils.h" #include "checksum.h" -#include "array.h" #define EXITCODE(rc) \ ( \ @@ -44,9 +44,8 @@ #define RB_FREEDIVES_END 0x4000 typedef struct mares_nemo_device_t { - device_t base; + mares_common_device_t base; struct serial *port; - unsigned char fingerprint[FP_SIZE]; } mares_nemo_device_t; static device_status_t mares_nemo_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size); @@ -65,6 +64,13 @@ static const device_backend_t mares_nemo_device_backend = { mares_nemo_device_close /* close */ }; +static const mares_common_layout_t mares_nemo_layout = { + 0x0070, /* rb_profile_begin */ + 0x3400, /* rb_profile_end */ + 0x3400, /* rb_freedives_begin */ + 0x4000 /* rb_freedives_end */ +}; + static int device_is_mares_nemo (device_t *abstract) { @@ -89,11 +95,10 @@ mares_nemo_device_open (device_t **out, const char* name) } // Initialize the base class. - device_init (&device->base, &mares_nemo_device_backend); + mares_common_device_init (&device->base, &mares_nemo_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); @@ -159,20 +164,12 @@ mares_nemo_device_close (device_t *abstract) static device_status_t mares_nemo_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) { - mares_nemo_device_t *device = (mares_nemo_device_t*) abstract; + mares_common_device_t *device = (mares_common_device_t*) abstract; if (! device_is_mares_nemo (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; + return mares_common_device_set_fingerprint (device, data, size); } @@ -281,113 +278,13 @@ mares_nemo_device_foreach (device_t *abstract, dive_callback_t callback, void *u device_status_t mares_nemo_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata) { - mares_nemo_device_t *device = (mares_nemo_device_t *) abstract; + mares_common_device_t *device = (mares_common_device_t*) abstract; if (abstract && !device_is_mares_nemo (abstract)) return DEVICE_STATUS_TYPE_MISMATCH; - assert (size >= MARES_NEMO_MEMORY_SIZE); + if (size < MARES_NEMO_MEMORY_SIZE) + return DEVICE_STATUS_ERROR; - // Get the end of the profile ring buffer. - unsigned int eop = array_uint16_le (data + 0x6B); - - // Make the ringbuffer linear, to avoid having to deal - // with the wrap point. The buffer has extra space to - // store the profile data for the freedives. - unsigned char buffer[RB_PROFILE_END - RB_PROFILE_BEGIN + RB_FREEDIVES_END - RB_FREEDIVES_BEGIN] = {0}; - memcpy (buffer + 0, data + eop, RB_PROFILE_END - eop); - memcpy (buffer + RB_PROFILE_END - eop, data + RB_PROFILE_BEGIN, eop - RB_PROFILE_BEGIN); - - // For a freedive session, the Mares Nemo stores all the freedives of - // that session in a single logbook entry, and each sample is actually - // a summary for each individual freedive in the session. The profile - // data is stored in a separate memory area. Since only the most recent - // recent freediving session can have profile data, we keep track of the - // number of freedives. - unsigned int nfreedives = 0; - - unsigned int offset = RB_PROFILE_END - RB_PROFILE_BEGIN; - while (offset >= 3) { - // Check the dive mode of the logbook entry. Valid modes are - // 0 (air), 1 (EANx), 2 (freedive) or 3 (bottom timer). - // If the ringbuffer has never reached the wrap point before, - // there will be "empty" memory (filled with 0xFF) and - // processing should stop at this point. - unsigned int mode = buffer[offset - 1]; - if (mode == 0xFF) - break; - - // The header and sample size are dependant on the dive mode. Only - // in freedive mode, the sizes are different from the other modes. - unsigned int header_size = 53; - unsigned int sample_size = 2; - if (mode == 2) { - header_size = 28; - sample_size = 6; - nfreedives++; - } - - // Get the number of samples in the profile data. - unsigned int nsamples = array_uint16_le (buffer + offset - 3); - - // Calculate the total number of bytes for this dive. - // If the buffer does not contain that much bytes, we reached the - // end of the ringbuffer. The current dive is incomplete (partially - // overwritten with newer data), and processing should stop. - unsigned int nbytes = 2 + nsamples * sample_size + header_size; - if (offset < nbytes) - break; - - // Move to the start of the dive. - offset -= nbytes; - - // Verify that the length that is stored in the profile data - // equals the calculated length. If both values are different, - // something is wrong and an error is returned. - unsigned int length = array_uint16_le (buffer + offset); - if (length != nbytes) { - WARNING ("Calculated and stored size are not equal."); - return DEVICE_STATUS_ERROR; - } - - // Process the profile data for the most recent freedive entry. - // Since we are processing the entries backwards (newest to oldest), - // this entry will always be the first one. - if (mode == 2 && nfreedives == 1) { - // Count the number of freedives in the profile data. - unsigned int count = 0; - unsigned int idx = RB_FREEDIVES_BEGIN; - while (idx + 2 <= RB_FREEDIVES_END && - count != nsamples) - { - // Each freedive in the session ends with a zero sample. - unsigned int sample = array_uint16_le (data + idx); - if (sample == 0) - count++; - - // Move to the next sample. - idx += 2; - } - - // Verify that the number of freedive entries in the session - // equals the number of freedives in the profile data. If - // both values are different, the profile data is incomplete. - assert (count == nsamples); - - // Append the profile data to the main logbook entry. The - // buffer is guaranteed to have enough space, and the dives - // that will be overwritten have already been processed. - memcpy (buffer + offset + nbytes, data + RB_FREEDIVES_BEGIN, idx - RB_FREEDIVES_BEGIN); - nbytes += idx - RB_FREEDIVES_BEGIN; - } - - unsigned int fp_offset = offset + length - FP_OFFSET; - if (device && memcmp (buffer + fp_offset, device->fingerprint, FP_SIZE) == 0) - return DEVICE_STATUS_SUCCESS; - - if (callback && !callback (buffer + offset, nbytes, userdata)) - return DEVICE_STATUS_SUCCESS; - } - - return DEVICE_STATUS_SUCCESS; + return mares_common_extract_dives (device, &mares_nemo_layout, data, callback, userdata); } diff --git a/src/mares_puck.c b/src/mares_puck.c index 8eb1eb0..eebe375 100644 --- a/src/mares_puck.c +++ b/src/mares_puck.c @@ -24,11 +24,11 @@ #include // assert #include "device-private.h" +#include "mares_common.h" #include "mares_puck.h" #include "serial.h" #include "utils.h" #include "checksum.h" -#include "array.h" #define EXITCODE(rc) \ ( \ @@ -38,25 +38,34 @@ #define MAXRETRIES 4 typedef struct mares_puck_device_t { - device_t base; + mares_common_device_t base; struct serial *port; } mares_puck_device_t; +static device_status_t mares_puck_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size); static device_status_t mares_puck_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static device_status_t mares_puck_device_dump (device_t *abstract, unsigned char data[], unsigned int size, unsigned int *result); +static device_status_t mares_puck_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata); static device_status_t mares_puck_device_close (device_t *abstract); static const device_backend_t mares_puck_device_backend = { DEVICE_TYPE_MARES_PUCK, - NULL, /* set_fingerprint */ + mares_puck_device_set_fingerprint, /* set_fingerprint */ NULL, /* version */ mares_puck_device_read, /* read */ NULL, /* write */ mares_puck_device_dump, /* dump */ - NULL, /* foreach */ + mares_puck_device_foreach, /* foreach */ mares_puck_device_close /* close */ }; +static const mares_common_layout_t mares_puck_layout = { + 0x0070, /* rb_profile_begin */ + 0x3400, /* rb_profile_end */ + 0x3400, /* rb_freedives_begin */ + 0x4000 /* rb_freedives_end */ +}; + static int device_is_mares_puck (device_t *abstract) { @@ -81,7 +90,7 @@ mares_puck_device_open (device_t **out, const char* name) } // Initialize the base class. - device_init (&device->base, &mares_puck_device_backend); + mares_common_device_init (&device->base, &mares_puck_device_backend); // Set the default values. device->port = NULL; @@ -271,6 +280,18 @@ mares_puck_transfer (mares_puck_device_t *device, const unsigned char command[], } +static device_status_t +mares_puck_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size) +{ + mares_common_device_t *device = (mares_common_device_t*) abstract; + + if (! device_is_mares_puck (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + return mares_common_device_set_fingerprint (device, data, size); +} + + static device_status_t mares_puck_device_read (device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) { @@ -337,3 +358,34 @@ mares_puck_device_dump (device_t *abstract, unsigned char data[], unsigned int s return DEVICE_STATUS_SUCCESS; } + + +static device_status_t +mares_puck_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata) +{ + if (! device_is_mares_puck (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + unsigned char data[MARES_PUCK_MEMORY_SIZE] = {0}; + + device_status_t rc = mares_puck_device_dump (abstract, data, sizeof (data), NULL); + if (rc != DEVICE_STATUS_SUCCESS) + return rc; + + return mares_puck_extract_dives (abstract, data, sizeof (data), callback, userdata); +} + + +device_status_t +mares_puck_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata) +{ + mares_common_device_t *device = (mares_common_device_t*) abstract; + + if (abstract && !device_is_mares_puck (abstract)) + return DEVICE_STATUS_TYPE_MISMATCH; + + if (size < MARES_PUCK_MEMORY_SIZE) + return DEVICE_STATUS_ERROR; + + return mares_common_extract_dives (device, &mares_puck_layout, data, callback, userdata); +} diff --git a/src/mares_puck.h b/src/mares_puck.h index 01e1efc..ff30831 100644 --- a/src/mares_puck.h +++ b/src/mares_puck.h @@ -35,6 +35,9 @@ extern "C" { device_status_t mares_puck_device_open (device_t **device, const char* name); +device_status_t +mares_puck_extract_dives (device_t *device, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata); + #ifdef __cplusplus } #endif /* __cplusplus */