Add a common base class and a layout descriptor structure.

The memory layout of the Suunto Eon and Vyper devices is very similar,
which allows to share the parsing code between the backends. Differences
in the layout are passed by means of a new layout descriptor structure
and a common base class is introduced to share the fingerprint data.
Memory buffers are now allocated dynamically to support devices with
different amounts of memory.
This commit is contained in:
Jef Driesen 2009-05-12 19:57:46 +00:00
parent 4c37c9c85f
commit 1e39b84075
4 changed files with 134 additions and 90 deletions

View File

@ -19,28 +19,66 @@
* MA 02110-1301 USA
*/
#include <assert.h>
#include <string.h>
#include <stdlib.h> // malloc
#include <string.h> // memcpy, memcmp
#include <assert.h> // assert
#include "suunto_common.h"
#include "ringbuffer.h"
#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, l->rb_profile_begin, l->rb_profile_end)
#define RB_PROFILE_PEEK(a,l) ringbuffer_decrement (a, l->peek, l->rb_profile_begin, l->rb_profile_end)
void
suunto_common_device_init (suunto_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
suunto_common_extract_dives (device_t *device, const unsigned char data[], unsigned int begin, unsigned int end, unsigned int eop, unsigned int peek, fp_compare_t fp_compare, dive_callback_t callback, void *userdata)
suunto_common_device_set_fingerprint (suunto_common_device_t *device, const unsigned char data[], unsigned int size)
{
assert (eop >= begin && eop < end);
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
suunto_common_extract_dives (suunto_common_device_t *device, const suunto_common_layout_t *layout, const unsigned char data[], unsigned int eop, dive_callback_t callback, void *userdata)
{
assert (layout != NULL);
assert (eop >= layout->rb_profile_begin && eop < layout->rb_profile_end);
assert (data[eop] == 0x82);
unsigned char buffer[0x2000 - 0x4C] = {0};
assert (sizeof (buffer) >= end - begin);
// Memory buffer for the profile ringbuffer.
unsigned int length = layout->rb_profile_end - layout->rb_profile_begin;
unsigned char *buffer = (unsigned char *) malloc (length);
if (buffer == NULL)
return DEVICE_STATUS_MEMORY;
unsigned int current = eop;
unsigned int previous = eop;
for (unsigned int i = 0; i < end - begin; ++i) {
for (unsigned int i = 0; i < length; ++i) {
// Move backwards through the ringbuffer.
if (current == begin)
current = end;
if (current == layout->rb_profile_begin)
current = layout->rb_profile_end;
current--;
// Check for an end of profile marker.
@ -49,28 +87,34 @@ suunto_common_extract_dives (device_t *device, const unsigned char data[], unsig
// Check for an end of dive marker (of the next dive),
// to find the start of the current dive.
unsigned int idx = ringbuffer_decrement (current, peek, begin, end);
unsigned int idx = RB_PROFILE_PEEK (current, layout);
if (data[idx] == 0x80) {
unsigned int len = ringbuffer_distance (current, previous, begin, end);
if (current + len > end) {
unsigned int a = end - current;
unsigned int b = (current + len) - end;
unsigned int len = RB_PROFILE_DISTANCE (current, previous, layout);
if (current + len > layout->rb_profile_end) {
unsigned int a = layout->rb_profile_end - current;
unsigned int b = (current + len) - layout->rb_profile_end;
memcpy (buffer + 0, data + current, a);
memcpy (buffer + a, data + begin, b);
memcpy (buffer + a, data + layout->rb_profile_begin, b);
} else {
memcpy (buffer, data + current, len);
}
if (device && fp_compare && fp_compare (device, buffer, len) == 0)
if (device && memcmp (buffer + layout->fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0) {
free (buffer);
return DEVICE_STATUS_SUCCESS;
}
if (callback && !callback (buffer, len, userdata))
if (callback && !callback (buffer, len, userdata)) {
free (buffer);
return DEVICE_STATUS_SUCCESS;
}
previous = current;
}
}
free (buffer);
assert (data[current] == 0x82);
return DEVICE_STATUS_SUCCESS;

View File

@ -22,16 +22,35 @@
#ifndef SUUNTO_COMMON_H
#define SUUNTO_COMMON_H
#include "device.h"
#include "device-private.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef int (*fp_compare_t) (device_t *device, const unsigned char data[], unsigned int size);
typedef struct suunto_common_device_t {
device_t base;
unsigned char fingerprint[5];
} suunto_common_device_t;
typedef struct suunto_common_layout_t {
// Profile ringbuffer
unsigned int rb_profile_begin;
unsigned int rb_profile_end;
// Fingerprint
unsigned int fp_offset;
// Peek
unsigned int peek;
} suunto_common_layout_t;
void
suunto_common_device_init (suunto_common_device_t *device, const device_backend_t *backend);
device_status_t
suunto_common_extract_dives (device_t *device, const unsigned char data[], unsigned int begin, unsigned int end, unsigned int eop, unsigned int peek, fp_compare_t fp_compare, dive_callback_t callback, void *userdata);
suunto_common_device_set_fingerprint (suunto_common_device_t *device, const unsigned char data[], unsigned int size);
device_status_t
suunto_common_extract_dives (suunto_common_device_t *device, const suunto_common_layout_t *layout, const unsigned char data[], unsigned int eop, dive_callback_t callback, void *userdata);
#ifdef __cplusplus
}

View File

@ -41,13 +41,9 @@
rc == -1 ? DEVICE_STATUS_IO : DEVICE_STATUS_TIMEOUT \
)
#define FP_OFFSET 6
#define FP_SIZE 5
typedef struct suunto_eon_device_t {
device_t base;
suunto_common_device_t base;
struct serial *port;
unsigned char fingerprint[FP_SIZE];
} suunto_eon_device_t;
static device_status_t suunto_eon_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size);
@ -66,6 +62,14 @@ static const device_backend_t suunto_eon_device_backend = {
suunto_eon_device_close /* close */
};
static const suunto_common_layout_t suunto_eon_layout = {
0x100, /* rb_profile_begin */
SUUNTO_EON_MEMORY_SIZE, /* rb_profile_end */
6, /* fp_offset */
3 /* peek */
};
static int
device_is_suunto_eon (device_t *abstract)
{
@ -90,11 +94,10 @@ suunto_eon_device_open (device_t **out, const char* name)
}
// Initialize the base class.
device_init (&device->base, &suunto_eon_device_backend);
suunto_common_device_init (&device->base, &suunto_eon_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 +162,12 @@ suunto_eon_device_close (device_t *abstract)
static device_status_t
suunto_eon_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size)
{
suunto_eon_device_t *device = (suunto_eon_device_t*) abstract;
suunto_common_device_t *device = (suunto_common_device_t*) abstract;
if (! device_is_suunto_eon (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 suunto_common_device_set_fingerprint (device, data, size);
}
@ -295,19 +290,16 @@ suunto_eon_device_write_interval (device_t *abstract, unsigned char interval)
}
static int
fp_compare (device_t *abstract, const unsigned char data[], unsigned int size)
{
suunto_eon_device_t *device = (suunto_eon_device_t*) abstract;
return memcmp (data + FP_OFFSET, device->fingerprint, FP_SIZE);
}
device_status_t
suunto_eon_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata)
{
assert (size >= SUUNTO_EON_MEMORY_SIZE);
suunto_common_device_t *device = (suunto_common_device_t*) abstract;
if (abstract && !device_is_suunto_eon (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
if (size < SUUNTO_EON_MEMORY_SIZE)
return DEVICE_STATUS_ERROR;
// Search the end-of-profile marker.
unsigned int eop = 0x100;
@ -318,5 +310,5 @@ suunto_eon_extract_dives (device_t *abstract, const unsigned char data[], unsign
eop++;
}
return suunto_common_extract_dives (abstract, data, 0x100, SUUNTO_EON_MEMORY_SIZE, eop, 3, fp_compare, callback, userdata);
return suunto_common_extract_dives (device, &suunto_eon_layout, data, eop, callback, userdata);
}

View File

@ -44,15 +44,10 @@
#define HDR_DEVINFO_BEGIN (HDR_DEVINFO_SPYDER)
#define HDR_DEVINFO_END (HDR_DEVINFO_VYPER + 6)
#define FP_OFFSET_VYPER 9
#define FP_OFFSET_SPYDER 7
#define FP_SIZE 5
typedef struct suunto_vyper_device_t {
device_t base;
suunto_common_device_t base;
struct serial *port;
unsigned int delay;
unsigned char fingerprint[FP_SIZE];
} suunto_vyper_device_t;
static device_status_t suunto_vyper_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size);
@ -73,6 +68,21 @@ static const device_backend_t suunto_vyper_device_backend = {
suunto_vyper_device_close /* close */
};
static const suunto_common_layout_t suunto_vyper_layout = {
0x71, /* rb_profile_begin */
SUUNTO_VYPER_MEMORY_SIZE, /* rb_profile_end */
9, /* fp_offset */
5 /* peek */
};
static const suunto_common_layout_t suunto_spyder_layout = {
0x4C, /* rb_profile_begin */
SUUNTO_VYPER_MEMORY_SIZE, /* rb_profile_end */
7, /* fp_offset */
3 /* peek */
};
static int
device_is_suunto_vyper (device_t *abstract)
{
@ -97,12 +107,11 @@ suunto_vyper_device_open (device_t **out, const char* name)
}
// Initialize the base class.
device_init (&device->base, &suunto_vyper_device_backend);
suunto_common_device_init (&device->base, &suunto_vyper_device_backend);
// Set the default values.
device->port = NULL;
device->delay = 500;
memset (device->fingerprint, 0, FP_SIZE);
// Open the device.
int rc = serial_open (&device->port, name);
@ -187,20 +196,12 @@ suunto_vyper_device_set_delay (device_t *abstract, unsigned int delay)
static device_status_t
suunto_vyper_device_set_fingerprint (device_t *abstract, const unsigned char data[], unsigned int size)
{
suunto_vyper_device_t *device = (suunto_vyper_device_t*) abstract;
suunto_common_device_t *device = (suunto_common_device_t*) abstract;
if (! device_is_suunto_vyper (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 suunto_common_device_set_fingerprint (device, data, size);
}
@ -561,7 +562,7 @@ suunto_vyper_device_dump (device_t *abstract, unsigned char data[], unsigned int
static device_status_t
suunto_vyper_device_foreach (device_t *abstract, dive_callback_t callback, void *userdata)
{
suunto_vyper_device_t *device = (suunto_vyper_device_t*) abstract;
suunto_common_device_t *device = (suunto_common_device_t*) abstract;
if (! device_is_suunto_vyper (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
@ -618,8 +619,8 @@ suunto_vyper_device_foreach (device_t *abstract, dive_callback_t callback, void
if (nbytes == 0)
return DEVICE_STATUS_SUCCESS;
unsigned int fp_offset = (vyper ? FP_OFFSET_VYPER : FP_OFFSET_SPYDER);
if (memcmp (data + offset + fp_offset, device->fingerprint, FP_SIZE) == 0)
unsigned int fp_offset = (vyper ? suunto_vyper_layout.fp_offset : suunto_spyder_layout.fp_offset);
if (memcmp (data + offset + fp_offset, device->fingerprint, sizeof (device->fingerprint)) == 0)
return DEVICE_STATUS_SUCCESS;
if (callback && !callback (data + offset, nbytes, userdata))
@ -633,28 +634,16 @@ suunto_vyper_device_foreach (device_t *abstract, dive_callback_t callback, void
}
static int
fp_compare_vyper (device_t *abstract, const unsigned char data[], unsigned int size)
{
suunto_vyper_device_t *device = (suunto_vyper_device_t*) abstract;
return memcmp (data + FP_OFFSET_VYPER, device->fingerprint, FP_SIZE);
}
static int
fp_compare_spyder (device_t *abstract, const unsigned char data[], unsigned int size)
{
suunto_vyper_device_t *device = (suunto_vyper_device_t*) abstract;
return memcmp (data + FP_OFFSET_SPYDER, device->fingerprint, FP_SIZE);
}
device_status_t
suunto_vyper_extract_dives (device_t *abstract, const unsigned char data[], unsigned int size, dive_callback_t callback, void *userdata)
{
assert (size >= SUUNTO_VYPER_MEMORY_SIZE);
suunto_common_device_t *device = (suunto_common_device_t*) abstract;
if (abstract && !device_is_suunto_vyper (abstract))
return DEVICE_STATUS_TYPE_MISMATCH;
if (size < SUUNTO_VYPER_MEMORY_SIZE)
return DEVICE_STATUS_ERROR;
unsigned int vyper = 1;
if (data[HDR_DEVINFO_VYPER] == 20 || data[HDR_DEVINFO_VYPER] == 30 || data[HDR_DEVINFO_VYPER] == 60)
@ -662,9 +651,9 @@ suunto_vyper_extract_dives (device_t *abstract, const unsigned char data[], unsi
if (vyper) {
unsigned int eop = array_uint16_be (data + 0x51);
return suunto_common_extract_dives (abstract, data, 0x71, SUUNTO_VYPER_MEMORY_SIZE, eop, 5, fp_compare_vyper, callback, userdata);
return suunto_common_extract_dives (device, &suunto_vyper_layout, data, eop, callback, userdata);
} else {
unsigned int eop = array_uint16_be (data + 0x1C);
return suunto_common_extract_dives (abstract, data, 0x4C, SUUNTO_VYPER_MEMORY_SIZE, eop, 3, fp_compare_spyder, callback, userdata);
return suunto_common_extract_dives (device, &suunto_spyder_layout, data, eop, callback, userdata);
}
}