diff --git a/examples/common.c b/examples/common.c index d5740d8..ebc1542 100644 --- a/examples/common.c +++ b/examples/common.c @@ -91,6 +91,7 @@ static const backend_table_t g_backends[] = { {"cochran", DC_FAMILY_COCHRAN_COMMANDER, 0}, {"divecomputereu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0}, {"descentmk1", DC_FAMILY_GARMIN, 0}, + {"cosmiq", DC_FAMILY_DEEPBLU, 0}, }; static const transport_table_t g_transports[] = { diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 4ba705b..0819843 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -110,6 +110,8 @@ typedef enum dc_family_t { DC_FAMILY_TECDIVING_DIVECOMPUTEREU = (15 << 16), /* Garmin */ DC_FAMILY_GARMIN = (16 << 16), + /* Deepblu */ + DC_FAMILY_DEEPBLU = (17 << 16), } dc_family_t; #ifdef __cplusplus diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj index 8c5cbf2..99abc2f 100644 --- a/msvc/libdivecomputer.vcproj +++ b/msvc/libdivecomputer.vcproj @@ -506,6 +506,14 @@ RelativePath="..\src\garmin_parser.c" > + + + + @@ -852,6 +860,10 @@ RelativePath="..\src\garmin.h" > + + diff --git a/src/Makefile.am b/src/Makefile.am index 2afd9cf..566352e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -73,6 +73,7 @@ libdivecomputer_la_SOURCES = \ cochran_commander.h cochran_commander.c cochran_commander_parser.c \ tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \ garmin.h garmin.c garmin_parser.c \ + deepblu.h deepblu.c deepblu_parser.c \ socket.h socket.c \ irda.c \ usbhid.c \ diff --git a/src/deepblu.c b/src/deepblu.c new file mode 100644 index 0000000..454486d --- /dev/null +++ b/src/deepblu.c @@ -0,0 +1,110 @@ +/* + * Deepblu Cosmiq+ downloading + * + * Copyright (C) 2019 Linus Torvalds + * + * 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 "deepblu.h" +#include "context-private.h" +#include "device-private.h" +#include "array.h" + +typedef struct deepblu_device_t { + dc_device_t base; + dc_iostream_t *iostream; + unsigned char fingerprint[8]; +} deepblu_device_t; + +static dc_status_t deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t deepblu_device_close (dc_device_t *abstract); + +static const dc_device_vtable_t deepblu_device_vtable = { + sizeof(deepblu_device_t), + DC_FAMILY_DEEPBLU, + deepblu_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + NULL, /* dump */ + deepblu_device_foreach, /* foreach */ + NULL, /* timesync */ + deepblu_device_close, /* close */ +}; + +dc_status_t +deepblu_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +{ + deepblu_device_t *device; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (deepblu_device_t *) dc_device_allocate (context, &deepblu_device_vtable); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Set the default values. + device->iostream = iostream; + memset(device->fingerprint, 0, sizeof(device->fingerprint)); + + *out = (dc_device_t *) device; + + ERROR (context, "Deepblu Cosmiq+ open called"); + return DC_STATUS_SUCCESS; +} + +static dc_status_t +deepblu_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + deepblu_device_t *device = (deepblu_device_t *)abstract; + + if (size && size != sizeof (device->fingerprint)) + return DC_STATUS_INVALIDARGS; + + if (size) + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + else + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + ERROR (device->base.context, "Deepblu Cosmiq+ set_fingerprint called"); + return DC_STATUS_SUCCESS; +} + + +static dc_status_t +deepblu_device_close (dc_device_t *abstract) +{ + deepblu_device_t *device = (deepblu_device_t *) abstract; + + ERROR (device->base.context, "Deepblu Cosmiq+ device close called"); + return DC_STATUS_SUCCESS; +} + +static dc_status_t +deepblu_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + deepblu_device_t *device = (deepblu_device_t *) abstract; + + ERROR (device->base.context, "Deepblu Cosmiq+ device_foreach called"); + return DC_STATUS_SUCCESS; +} diff --git a/src/deepblu.h b/src/deepblu.h new file mode 100644 index 0000000..92b821f --- /dev/null +++ b/src/deepblu.h @@ -0,0 +1,43 @@ +/* + * Deepblu Cosmiq+ downloading/parsing + * + * Copyright (C) 2018 Linus Torvalds + * + * 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 DEEPBLU_H +#define DEEPBLU_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +deepblu_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); + +dc_status_t +deepblu_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DEEPBLU_H */ diff --git a/src/deepblu_parser.c b/src/deepblu_parser.c new file mode 100644 index 0000000..71161e1 --- /dev/null +++ b/src/deepblu_parser.c @@ -0,0 +1,240 @@ +/* + * Deeplu Cosmiq+ parsing + * + * Copyright (C) 2019 Linus Torvalds + * + * 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 + +#include "deepblu.h" +#include "context-private.h" +#include "parser-private.h" +#include "array.h" + +#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +#define MAXFIELDS 128 + +struct msg_desc; + +#define MAXTYPE 16 +#define MAXGASES 16 +#define MAXSTRINGS 32 + +typedef struct deepblu_parser_t { + dc_parser_t base; + + dc_sample_callback_t callback; + void *userdata; + + // Field cache + struct { + unsigned int initialized; + + // dc_get_field() data + unsigned int DIVETIME; + double MAXDEPTH; + double AVGDEPTH; + unsigned int GASMIX_COUNT; + dc_salinity_t SALINITY; + dc_gasmix_t gasmix[MAXGASES]; + + dc_field_string_t strings[MAXSTRINGS]; + } cache; +} deepblu_parser_t; + +// I *really* need to make this generic +static void add_string(deepblu_parser_t *deepblu, const char *desc, const char *data); +static void add_string_fmt(deepblu_parser_t *deepblu, const char *desc, const char *fmt, ...); + +/* + * Macro to make it easy to set DC_FIELD_xyz values + */ +#define ASSIGN_FIELD(name, value) do { \ + deepblu->cache.initialized |= 1u << DC_FIELD_##name; \ + deepblu->cache.name = (value); \ +} while (0) + + +static dc_status_t deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); + +static const dc_parser_vtable_t deepblu_parser_vtable = { + sizeof(deepblu_parser_t), + DC_FAMILY_DEEPBLU, + deepblu_parser_set_data, /* set_data */ + deepblu_parser_get_datetime, /* datetime */ + deepblu_parser_get_field, /* fields */ + deepblu_parser_samples_foreach, /* samples_foreach */ + NULL /* destroy */ +}; + +dc_status_t +deepblu_parser_create (dc_parser_t **out, dc_context_t *context) +{ + deepblu_parser_t *parser = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + parser = (deepblu_parser_t *) dc_parser_allocate (context, &deepblu_parser_vtable); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + *out = (dc_parser_t *) parser; + + ERROR (context, "Deepblu Cosmiq+ parser_create() called"); + return DC_STATUS_SUCCESS; +} + +/* + * FIXME! This should all be generic. + * + * Now it's just copied between all the different + * dive computers that support the strings.. + */ +static void add_string(deepblu_parser_t *deepblu, const char *desc, const char *value) +{ + int i; + + deepblu->cache.initialized |= 1 << DC_FIELD_STRING; + for (i = 0; i < MAXSTRINGS; i++) { + dc_field_string_t *str = deepblu->cache.strings+i; + if (str->desc) + continue; + str->desc = desc; + str->value = strdup(value); + break; + } +} + +static void add_string_fmt(deepblu_parser_t *deepblu, const char *desc, const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + + va_start(ap, fmt); + buffer[sizeof(buffer)-1] = 0; + (void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); + va_end(ap); + + add_string(deepblu, desc, buffer); +} + +static dc_status_t +deepblu_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract; + + deepblu->callback = NULL; + deepblu->userdata = NULL; + memset(&deepblu->cache, 0, sizeof(deepblu->cache)); + + ERROR (abstract->context, "Deepblu Cosmiq+ parser_set_data() called"); + return DC_STATUS_SUCCESS; +} + +static dc_status_t +deepblu_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) +{ + deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract; + + ERROR (abstract->context, "Deepblu Cosmiq+ parser_get_datetime() called"); + return DC_STATUS_UNSUPPORTED; +} + +static dc_status_t get_string_field(dc_field_string_t *strings, unsigned idx, dc_field_string_t *value) +{ + if (idx < MAXSTRINGS) { + dc_field_string_t *res = strings+idx; + if (res->desc && res->value) { + *value = *res; + return DC_STATUS_SUCCESS; + } + } + return DC_STATUS_UNSUPPORTED; +} + +// Ugly define thing makes the code much easier to read +// I'd love to use __typeof__, but that's a gcc'ism +#define field_value(p, NAME) \ + (memcpy((p), &deepblu->cache.NAME, sizeof(deepblu->cache.NAME)), DC_STATUS_SUCCESS) +// Hacky hack hack +#define GASMIX gasmix[flags] + +static dc_status_t +deepblu_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) +{ + deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract; + + if (!value) + return DC_STATUS_INVALIDARGS; + + /* This whole sequence should be standardized */ + if (!(deepblu->cache.initialized & (1 << type))) + return DC_STATUS_UNSUPPORTED; + + switch (type) { + case DC_FIELD_DIVETIME: + return field_value(value, DIVETIME); + case DC_FIELD_MAXDEPTH: + return field_value(value, MAXDEPTH); + case DC_FIELD_AVGDEPTH: + return field_value(value, AVGDEPTH); + case DC_FIELD_GASMIX_COUNT: + case DC_FIELD_TANK_COUNT: + return field_value(value, GASMIX_COUNT); + case DC_FIELD_GASMIX: + if (flags >= MAXGASES) + return DC_STATUS_UNSUPPORTED; + return field_value(value, GASMIX); + case DC_FIELD_SALINITY: + return field_value(value, SALINITY); + case DC_FIELD_ATMOSPHERIC: + return DC_STATUS_UNSUPPORTED; + case DC_FIELD_DIVEMODE: + return DC_STATUS_UNSUPPORTED; + case DC_FIELD_TANK: + return DC_STATUS_UNSUPPORTED; + case DC_FIELD_STRING: + return get_string_field(deepblu->cache.strings, flags, (dc_field_string_t *)value); + default: + return DC_STATUS_UNSUPPORTED; + } + return DC_STATUS_SUCCESS; +} + +static dc_status_t +deepblu_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + deepblu_parser_t *deepblu = (deepblu_parser_t *) abstract; + + deepblu->callback = callback; + deepblu->userdata = userdata; + + ERROR (abstract->context, "Deepblu Cosmiq+ samples_foreach() called"); + return DC_STATUS_SUCCESS; +} diff --git a/src/descriptor.c b/src/descriptor.c index da0a990..81d34f0 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -48,6 +48,7 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata); static int dc_filter_garmin (dc_transport_t transport, const void *userdata); static int dc_filter_mares (dc_transport_t transport, const void *userdata); static int dc_filter_divesystem (dc_transport_t transport, const void *userdata); +static int dc_filter_deepblu (dc_transport_t transport, const void *userdata); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -373,6 +374,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Tecdiving", "DiveComputer.eu", DC_FAMILY_TECDIVING_DIVECOMPUTEREU, 0, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLUETOOTH, dc_filter_tecdiving}, /* Garmin */ {"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin}, + /* Deepblu */ + {"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU, 0, DC_TRANSPORT_BLE, dc_filter_deepblu}, }; static int @@ -601,6 +604,19 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata) return 1; } +static int dc_filter_deepblu (dc_transport_t transport, const void *userdata) +{ + static const char * const bluetooth[] = { + "COSMIQ", + }; + + if (transport == DC_TRANSPORT_BLE) { + return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name); + } + + return 1; +} + dc_status_t dc_descriptor_iterator (dc_iterator_t **out) { diff --git a/src/device.c b/src/device.c index ce9a29f..15bfcf3 100644 --- a/src/device.c +++ b/src/device.c @@ -58,6 +58,7 @@ #include "cochran_commander.h" #include "tecdiving_divecomputereu.h" #include "garmin.h" +#include "deepblu.h" #include "device-private.h" #include "context-private.h" @@ -215,6 +216,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_GARMIN: rc = garmin_device_open (&device, context, iostream); break; + case DC_FAMILY_DEEPBLU: + rc = deepblu_device_open (&device, context, iostream); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/parser.c b/src/parser.c index 563bd78..11e3975 100644 --- a/src/parser.c +++ b/src/parser.c @@ -176,6 +176,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa case DC_FAMILY_GARMIN: rc = garmin_parser_create (&parser, context); break; + case DC_FAMILY_DEEPBLU: + rc = deepblu_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; }