From d1106cb8babb993d97b816f8274e22ae4fa6a08d Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 14 Jun 2024 13:50:39 -0400 Subject: [PATCH] Add support for the Aqualung i330R and Apeks DSX --- src/Makefile.am | 11 +- src/oceanic_atom2_parser.c | 242 ++++++++++---- src/oceanic_common.c | 382 ++++++++++++---------- src/oceanic_common.h | 21 +- src/parser.c | 31 +- src/pelagic_i330r.c | 646 +++++++++++++++++++++++++++++++++++++ src/pelagic_i330r.h | 40 +++ 7 files changed, 1113 insertions(+), 260 deletions(-) create mode 100644 src/pelagic_i330r.c create mode 100644 src/pelagic_i330r.h diff --git a/src/Makefile.am b/src/Makefile.am index 2139959..16680d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,9 @@ AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include -AM_CFLAGS = $(LIBUSB_CFLAGS) $(LIBMTP_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS) +AM_CFLAGS = $(LIBUSB_CFLAGS) $(HIDAPI_CFLAGS) $(BLUEZ_CFLAGS) lib_LTLIBRARIES = libdivecomputer.la -libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(LIBMTP_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm +libdivecomputer_la_LIBADD = $(LIBUSB_LIBS) $(HIDAPI_LIBS) $(BLUEZ_LIBS) -lm libdivecomputer_la_LDFLAGS = \ -version-info $(DC_VERSION_LIBTOOL) \ -no-undefined \ @@ -43,6 +43,7 @@ libdivecomputer_la_SOURCES = \ oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \ oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \ oceanic_vtpro.h oceanic_vtpro.c oceanic_vtpro_parser.c \ + pelagic_i330r.h pelagic_i330r.c \ mares_common.h mares_common.c \ mares_nemo.h mares_nemo.c mares_nemo_parser.c \ mares_puck.h mares_puck.c \ @@ -90,12 +91,6 @@ libdivecomputer_la_SOURCES = \ bluetooth.c \ custom.c -# Not merged upstream yet -libdivecomputer_la_SOURCES += \ - usb_storage.c \ - field-cache.h field-cache.c \ - garmin.h garmin.c garmin_parser.c - if OS_WIN32 libdivecomputer_la_SOURCES += serial_win32.c else diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index fe50796..19b7a91 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -20,8 +20,6 @@ */ #include -#include -#include #include @@ -37,6 +35,12 @@ #define GAUGE 1 #define FREEDIVE 2 +#define DSX_CC 0 +#define DSX_OC 1 +#define DSX_SIDEMOUNT 2 +#define DSX_SIDEGAUGE 3 +#define DSX_GAUGE 4 + #define NGASMIXES 6 #define HEADER 1 @@ -47,9 +51,9 @@ typedef struct oceanic_atom2_parser_t oceanic_atom2_parser_t; struct oceanic_atom2_parser_t { dc_parser_t base; unsigned int model; + unsigned int logbooksize; unsigned int headersize; unsigned int footersize; - unsigned int serial; // Cached fields. unsigned int cached; unsigned int header; @@ -80,7 +84,7 @@ static const dc_parser_vtable_t oceanic_atom2_parser_vtable = { dc_status_t -oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model, unsigned int serial) +oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model) { oceanic_atom2_parser_t *parser = NULL; @@ -96,6 +100,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, const uns // Set the default values. parser->model = model; + parser->logbooksize = 0; parser->headersize = 9 * PAGESIZE / 2; parser->footersize = 2 * PAGESIZE / 2; if (model == DATAMASK || model == COMPUMASK || @@ -134,9 +139,16 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, const uns } else if (model == I550C || model == WISDOM4 || model == I200CV2) { parser->headersize = 5 * PAGESIZE / 2; + } else if (model == I330R) { + parser->logbooksize = 64; + parser->headersize = parser->logbooksize + 80; + parser->footersize = 48; + } else if (model == DSX) { + parser->logbooksize = 512; + parser->headersize = parser->logbooksize + 256; + parser->footersize = 64; } - parser->serial = serial; parser->cached = 0; parser->header = 0; parser->footer = 0; @@ -174,8 +186,18 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim if (datetime) { // AM/PM bit of the 12-hour clock. unsigned int pm = p[1] & 0x80; + unsigned int have_ampm = 1; switch (parser->model) { + case I330R: + case DSX: + datetime->year = p[7] + 2000; + datetime->month = p[6]; + datetime->day = p[5]; + datetime->hour = p[3]; + datetime->minute = p[4]; + have_ampm = 0; + break; case OC1A: case OC1B: case OC1C: @@ -284,9 +306,11 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim datetime->timezone = DC_TIMEZONE_NONE; // Convert to a 24-hour clock. - datetime->hour %= 12; - if (pm) - datetime->hour += 12; + if (have_ampm) { + datetime->hour %= 12; + if (pm) + datetime->hour += 12; + } /* * Workaround for the year 2010 problem. @@ -323,7 +347,6 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim return DC_STATUS_SUCCESS; } -#define BUF_LEN 16 static dc_status_t oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) @@ -362,6 +385,10 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) } else if (parser->model == VEO20 || parser->model == VEO30 || parser->model == OCS) { mode = (data[1] & 0x60) >> 5; + } else if (parser->model == I330R) { + mode = data[2]; + } else if (parser->model == DSX) { + mode = data[45]; } // Get the gas mixes. @@ -419,6 +446,17 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) } else if (parser->model == WISDOM4) { o2_offset = header + 4; ngasmixes = 1; + } else if (parser->model == I330R) { + ngasmixes = 3; + o2_offset = parser->logbooksize + 16; + } else if (parser->model == DSX) { + if (mode < DSX_SIDEGAUGE) { + o2_offset = parser->logbooksize + 0x89 + mode * 16; + he_offset = parser->logbooksize + 0xB9 + mode * 16; + ngasmixes = 6; + } else { + ngasmixes = 0; + } } else { o2_offset = header + 4; ngasmixes = 3; @@ -432,6 +470,10 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) for (unsigned int i = 0; i < ngasmixes; ++i) { if (data[o2_offset + i * o2_step]) { parser->oxygen[i] = data[o2_offset + i * o2_step]; + // The i330R uses 20 as "Air" and 21 as 21% Nitrox + if (parser->model == I330R && parser->oxygen[i] == 20) { + parser->oxygen[i] = 21; + } } else { parser->oxygen[i] = 21; } @@ -474,9 +516,6 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; - dc_field_string_t *string = (dc_field_string_t *) value; - - char buf[BUF_LEN]; if (value) { switch (type) { @@ -493,9 +532,18 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns parser->model == F11A || parser->model == F11B || parser->model == MUNDIAL2 || parser->model == MUNDIAL3) *((double *) value) = array_uint16_le (data + 4) / 16.0 * FEET; + else if (parser->model == I330R || parser->model == DSX) + *((double *) value) = array_uint16_le (data + parser->footer + 10) / 10.0 * FEET; else *((double *) value) = (array_uint16_le (data + parser->footer + 4) & 0x0FFF) / 16.0 * FEET; break; + case DC_FIELD_AVGDEPTH: + if (parser->model == I330R || parser->model == DSX) { + *((double *) value) = array_uint16_le (data + parser->footer + 12) / 10.0 * FEET; + } else { + return DC_STATUS_UNSUPPORTED; + } + break; case DC_FIELD_GASMIX_COUNT: *((unsigned int *) value) = parser->ngasmixes; break; @@ -507,43 +555,58 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns break; case DC_FIELD_SALINITY: if (parser->model == A300CS || parser->model == VTX || - parser->model == I750TC || parser->model == I770R) { + parser->model == I750TC) { if (data[0x18] & 0x80) { water->type = DC_WATER_FRESH; } else { water->type = DC_WATER_SALT; } water->density = 0.0; + } else if (parser->model == I330R || parser->model == DSX) { + unsigned int settings = array_uint32_le (data + parser->logbooksize + 12); + if (settings & 0x10000) { + water->type = DC_WATER_FRESH; + } else { + water->type = DC_WATER_SALT; + } + water->density = 0.0; } else { return DC_STATUS_UNSUPPORTED; } break; case DC_FIELD_DIVEMODE: - switch (parser->mode) { - case NORMAL: - *((unsigned int *) value) = DC_DIVEMODE_OC; - break; - case GAUGE: - *((unsigned int *) value) = DC_DIVEMODE_GAUGE; - break; - case FREEDIVE: - *((unsigned int *) value) = DC_DIVEMODE_FREEDIVE; - break; - default: - return DC_STATUS_DATAFORMAT; + if (parser->model == DSX) { + switch (parser->mode) { + case DSX_OC: + case DSX_SIDEMOUNT: + *((unsigned int *) value) = DC_DIVEMODE_OC; + break; + case DSX_SIDEGAUGE: + case DSX_GAUGE: + *((unsigned int *) value) = DC_DIVEMODE_GAUGE; + break; + case DSX_CC: + *((unsigned int *) value) = DC_DIVEMODE_CCR; + break; + default: + return DC_STATUS_DATAFORMAT; + } + } else { + switch (parser->mode) { + case NORMAL: + *((unsigned int *) value) = DC_DIVEMODE_OC; + break; + case GAUGE: + *((unsigned int *) value) = DC_DIVEMODE_GAUGE; + break; + case FREEDIVE: + *((unsigned int *) value) = DC_DIVEMODE_FREEDIVE; + break; + default: + return DC_STATUS_DATAFORMAT; + } } break; - case DC_FIELD_STRING: - switch(flags) { - case 0: /* Serial */ - string->desc = "Serial"; - snprintf(buf, BUF_LEN, "%06u", parser->serial); - break; - default: - return DC_STATUS_UNSUPPORTED; - } - string->value = strdup(buf); - break; default: return DC_STATUS_UNSUPPORTED; } @@ -606,15 +669,19 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int time = 0; unsigned int interval = 1000; if (parser->mode != FREEDIVE) { - unsigned int offset = 0x17; - if (parser->model == A300CS || parser->model == VTX || - parser->model == I450T || parser->model == I750TC || - parser->model == PROPLUSX || parser->model == I770R || - parser->model == SAGE || parser->model == BEACON) - offset = 0x1f; - const unsigned int intervals[] = {2000, 15000, 30000, 60000}; - unsigned int idx = data[offset] & 0x03; - interval = intervals[idx]; + if (parser->model == I330R || parser->model == DSX) { + interval = data[parser->logbooksize + 36] * 1000; + } else { + unsigned int offset = 0x17; + if (parser->model == A300CS || parser->model == VTX || + parser->model == I450T || parser->model == I750TC || + parser->model == PROPLUSX || parser->model == I770R || + parser->model == SAGE || parser->model == BEACON) + offset = 0x1f; + const unsigned int intervals[] = {2000, 15000, 30000, 60000}; + unsigned int idx = data[offset] & 0x03; + interval = intervals[idx]; + } } else if (parser->model == F11A || parser->model == F11B) { const unsigned int intervals[] = {250, 500, 1000, 2000}; unsigned int idx = data[0x29] & 0x03; @@ -637,8 +704,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == I750TC || parser->model == PROPLUSX || parser->model == I770R || parser->model == I470TC || parser->model == SAGE || parser->model == BEACON || - parser->model == GEOAIR) { + parser->model == GEOAIR || parser->model == I330R) { samplesize = PAGESIZE; + } else if (parser->model == DSX) { + samplesize = 32; } unsigned int have_temperature = 1, have_pressure = 1; @@ -653,7 +722,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == I200 || parser->model == I100 || parser->model == I300C || parser->model == TALIS || parser->model == I200C || parser->model == I200CV2 || - parser->model == GEO40 || parser->model == VEO40) { + parser->model == GEO40 || parser->model == VEO40 || + parser->model == I330R) { have_pressure = 0; } @@ -664,12 +734,12 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } // Initial tank pressure. - unsigned int tank = 0; + unsigned int tank = 1; unsigned int pressure = 0; if (have_pressure) { unsigned int idx = 2; if (parser->model == A300CS || parser->model == VTX || - parser->model == I750TC || parser->model == I770R) + parser->model == I750TC) idx = 16; pressure = array_uint16_le(data + parser->header + idx); if (pressure == 10000) @@ -719,17 +789,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (sampletype == 0xAA) { if (parser->model == DATAMASK || parser->model == COMPUMASK) { // Tank pressure (1 psi) and number - tank = 0; + tank = 1; pressure = (((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF); } else if (parser->model == A300CS || parser->model == VTX || - parser->model == I750TC || parser->model == I770R || - parser->model == SAGE || parser->model == BEACON) { + parser->model == I750TC || parser->model == SAGE || + parser->model == BEACON) { // Tank pressure (1 psi) and number (one based index) - tank = (data[offset + 1] & 0x03) - 1; + tank = data[offset + 1] & 0x03; pressure = ((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF; } else { // Tank pressure (2 psi) and number (one based index) - tank = (data[offset + 1] & 0x03) - 1; + tank = data[offset + 1] & 0x03; if (parser->model == ATOM2 || parser->model == EPICA || parser->model == EPICB) pressure = (((data[offset + 3] << 8) + data[offset + 4]) & 0x0FFF) * 2; else @@ -822,6 +892,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == I770R|| parser->model == SAGE || parser->model == BEACON) { temperature = data[offset + 11]; + } else if (parser->model == I330R || parser->model == DSX) { + temperature = array_uint16_le(data + offset + 10); } else { unsigned int sign; if (parser->model == DG03 || parser->model == PROPLUS3 || @@ -844,7 +916,11 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ else temperature += (data[offset + 7] & 0x0C) >> 2; } - sample.temperature = (temperature - 32.0) * (5.0 / 9.0); + if (parser->model == I330R || parser->model == DSX) { + sample.temperature = ((temperature / 10.0) - 32.0) * (5.0 / 9.0); + } else { + sample.temperature = (temperature - 32.0) * (5.0 / 9.0); + } if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata); } @@ -869,11 +945,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == PROPLUSX || parser->model == I770R || parser->model == SAGE || parser->model == BEACON) pressure = array_uint16_le (data + offset + 4); - else + else if (parser->model == DSX) { + pressure = array_uint16_le (data + offset + 14); + tank = (data[offset] & 0xF0) >> 4; + } else { pressure -= data[offset + 1]; - sample.pressure.tank = tank; - sample.pressure.value = pressure * PSI / BAR; - if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); + } + if (tank) { + sample.pressure.tank = tank - 1; + sample.pressure.value = pressure * PSI / BAR; + if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); + } } // Depth (1/16 ft) @@ -891,11 +973,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ parser->model == I470TC || parser->model == I200CV2 || parser->model == GEOAIR) depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF; + else if (parser->model == I330R || parser->model == DSX) + depth = array_uint16_le (data + offset + 2); else if (parser->model == ATOM1) depth = data[offset + 3] * 16; else depth = (data[offset + 2] + (data[offset + 3] << 8)) & 0x0FFF; - sample.depth = depth / 16.0 * FEET; + if (parser->model == I330R || parser->model == DSX) { + sample.depth = depth / 10.0 * FEET; + } else { + sample.depth = depth / 16.0 * FEET; + } if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata); // Gas mix @@ -904,8 +992,11 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (parser->model == TX1) { gasmix = data[offset] & 0x07; have_gasmix = 1; + } else if (parser->model == DSX) { + gasmix = (data[offset] & 0xF0) >> 4; + have_gasmix = 1; } - if (have_gasmix && gasmix != gasmix_previous) { + if (have_gasmix && gasmix != gasmix_previous && parser->ngasmixes > 0) { if (gasmix < 1 || gasmix > parser->ngasmixes) { ERROR (abstract->context, "Invalid gas mix index (%u).", gasmix); return DC_STATUS_DATAFORMAT; @@ -935,7 +1026,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ have_deco = 1; } else if (parser->model == ATOM31 || parser->model == VISION || parser->model == XPAIR || parser->model == I550 || - parser->model == I550C || parser->model == WISDOM4) { + parser->model == I550C || parser->model == WISDOM4 || + parser->model == PROPLUS4 || parser->model == ATMOSAI2) { decostop = (data[offset + 5] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 4) & 0x03FF; have_deco = 1; @@ -950,11 +1042,25 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ decostop = (data[offset + 7] & 0xF0) >> 4; decotime = array_uint16_le(data + offset + 6) & 0x0FFF; have_deco = 1; + } else if (parser->model == I330R || parser->model == DSX) { + decostop = data[offset + 8]; + if (decostop) { + // Deco time + decotime = array_uint16_le(data + offset + 6); + } else { + // NDL + decotime = array_uint16_le(data + offset + 4); + } + have_deco = 1; } if (have_deco) { if (decostop) { sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.depth = decostop * 10 * FEET; + if (parser->model == I330R || parser->model == DSX) { + sample.deco.depth = decostop * FEET; + } else { + sample.deco.depth = decostop * 10 * FEET; + } } else { sample.deco.type = DC_DECO_NDL; sample.deco.depth = 0.0; @@ -978,7 +1084,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ have_rbt = 1; } else if (parser->model == VISION || parser->model == XPAIR || parser->model == I550 || parser->model == I550C || - parser->model == WISDOM4) { + parser->model == WISDOM4 || parser->model == PROPLUS4 || + parser->model == ATMOSAI2) { rbt = array_uint16_le(data + offset + 6) & 0x03FF; have_rbt = 1; } @@ -987,6 +1094,13 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ if (callback) callback (DC_SAMPLE_RBT, &sample, userdata); } + // PPO2 + if (parser->model == I330R) { + sample.ppo2.sensor = DC_SENSOR_NONE; + sample.ppo2.value = data[offset + 9] / 100.0; + if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata); + } + // Bookmarks unsigned int have_bookmark = 0; if (parser->model == OC1A || parser->model == OC1B || diff --git a/src/oceanic_common.c b/src/oceanic_common.c index 62f5f74..fc80bc1 100644 --- a/src/oceanic_common.c +++ b/src/oceanic_common.c @@ -32,72 +32,62 @@ #define VTABLE(abstract) ((const oceanic_common_device_vtable_t *) abstract->vtable) -#define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 1, l->rb_logbook_begin, l->rb_logbook_end) -#define RB_LOGBOOK_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_logbook_begin, l->rb_logbook_end) +#define RB_LOGBOOK_DISTANCE(a,b,l,m) ringbuffer_distance (a, b, m, l->rb_logbook_begin, l->rb_logbook_end) -#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_profile_begin, l->rb_profile_end) -#define RB_PROFILE_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_profile_begin, l->rb_profile_end) +#define RB_PROFILE_DISTANCE(a,b,l,m) ringbuffer_distance (a, b, m, l->rb_profile_begin, l->rb_profile_end) #define INVALID 0 -static unsigned int -get_profile_first (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int pagesize) +static dc_status_t +oceanic_common_device_get_profile (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int *begin, unsigned int *end) { - unsigned int value; + assert (layout != NULL); + assert (begin != NULL && end != NULL); + // Get the pagesize + unsigned int pagesize = layout->highmem ? 16 * PAGESIZE : PAGESIZE; + + // Get the profile pointers. + unsigned int first = 0, last = 0; if (layout->pt_mode_logbook == 0) { - value = array_uint16_le (data + 5); + first = array_uint16_le (data + 5); + last = array_uint16_le (data + 6) >> 4; } else if (layout->pt_mode_logbook == 1) { - value = array_uint16_le (data + 4); - } else if (layout->pt_mode_logbook == 3) { - value = array_uint16_le (data + 16); - } else { - return array_uint16_le (data + 16); + first = array_uint16_le (data + 4); + last = array_uint16_le (data + 6); + } else if (layout->pt_mode_logbook == 2 || layout->pt_mode_logbook == 3) { + first = array_uint16_le (data + 16); + last = array_uint16_le (data + 18); + } else if (layout->pt_mode_logbook == 4) { + first = array_uint32_le (data + 8); + last = array_uint32_le (data + 12); } - unsigned int npages = (layout->memsize - layout->highmem) / pagesize; - if (npages > 0x4000) { - value &= 0x7FFF; - } else if (npages > 0x2000) { - value &= 0x3FFF; - } else if (npages > 0x1000) { - value &= 0x1FFF; - } else { - value &= 0x0FFF; + // Convert pages to bytes. + if (layout->pt_mode_logbook < 3) { + unsigned int npages = (layout->memsize - layout->highmem) / pagesize; + if (npages > 0x4000) { + first &= 0x7FFF; + last &= 0x7FFF; + } else if (npages > 0x2000) { + first &= 0x3FFF; + last &= 0x3FFF; + } else if (npages > 0x1000) { + first &= 0x1FFF; + last &= 0x1FFF; + } else { + first &= 0x0FFF; + last &= 0x0FFF; + } + + first *= pagesize; + last *= pagesize; } - return layout->highmem + value * pagesize; -} + *begin = layout->highmem + first; + *end = layout->highmem + last + (layout->pt_mode_logbook < 4 ? pagesize : 0); - -static unsigned int -get_profile_last (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int pagesize) -{ - unsigned int value; - - if (layout->pt_mode_logbook == 0) { - value = array_uint16_le (data + 6) >> 4; - } else if (layout->pt_mode_logbook == 1) { - value = array_uint16_le (data + 6); - } else if (layout->pt_mode_logbook == 3) { - value = array_uint16_le (data + 18); - } else { - return array_uint16_le(data + 18); - } - - unsigned int npages = (layout->memsize - layout->highmem) / pagesize; - - if (npages > 0x4000) { - value &= 0x7FFF; - } else if (npages > 0x2000) { - value &= 0x3FFF; - } else if (npages > 0x1000) { - value &= 0x1FFF; - } else { - value &= 0x0FFF; - } - - return layout->highmem + value * pagesize; + return DC_STATUS_SUCCESS; } @@ -206,11 +196,11 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return DC_STATUS_NOMEMORY; } - // Emit a vendor event. - dc_event_vendor_t vendor; - vendor.data = device->version; - vendor.size = sizeof (device->version); - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + // Read the device info. + status = VTABLE(abstract)->devinfo (abstract, NULL); + if (status != DC_STATUS_SUCCESS) { + return status; + } // Download the memory dump. status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer), @@ -219,8 +209,43 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) return status; } + return status; +} + + +dc_status_t +oceanic_common_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progress) +{ + dc_status_t status = DC_STATUS_SUCCESS; + oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; + + assert (device != NULL); + assert (device->layout != NULL); + + const oceanic_common_layout_t *layout = device->layout; + + // Read the device id. + unsigned char id[PAGESIZE] = {0}; + status = dc_device_read (abstract, layout->cf_devinfo, id, sizeof (id)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory page."); + return status; + } + + // Update and emit a progress event. + if (progress) { + progress->current += PAGESIZE; + progress->maximum += PAGESIZE; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + // Emit a vendor event. + dc_event_vendor_t vendor; + vendor.data = device->version; + vendor.size = sizeof (device->version); + device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); + // Emit a device info event. - unsigned char *id = dc_buffer_get_data (buffer) + layout->cf_devinfo; dc_event_devinfo_t devinfo; devinfo.model = array_uint16_be (id + 8); devinfo.firmware = device->firmware; @@ -240,7 +265,51 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) dc_status_t -oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook) +oceanic_common_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, + unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, + unsigned int *rb_profile_begin, unsigned int *rb_profile_end) +{ + dc_status_t status = DC_STATUS_SUCCESS; + oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; + + assert (device != NULL); + assert (device->layout != NULL); + assert (rb_logbook_begin != NULL && rb_logbook_end != NULL); + assert (rb_profile_begin != NULL && rb_profile_end != NULL); + + const oceanic_common_layout_t *layout = device->layout; + + // Read the pointer data. + unsigned char pointers[PAGESIZE] = {0}; + status = dc_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory page."); + return status; + } + + // Update and emit a progress event. + if (progress) { + progress->current += PAGESIZE; + progress->maximum += PAGESIZE; + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + // Get the pointers. + unsigned int rb_logbook_first = array_uint16_le (pointers + 4); + unsigned int rb_logbook_last = array_uint16_le (pointers + 6); + unsigned int rb_profile_first = array_uint16_le (pointers + 8); + unsigned int rb_profile_last = array_uint16_le (pointers + 10); + + *rb_logbook_begin = rb_logbook_first; + *rb_logbook_end = rb_logbook_last + (layout->pt_mode_global == 0 ? layout->rb_logbook_entry_size : 0); + *rb_profile_begin = rb_profile_first; + *rb_profile_end = rb_profile_last; + + return status; +} + +dc_status_t +oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end) { oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; dc_status_t rc = DC_STATUS_SUCCESS; @@ -256,37 +325,30 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr if (!dc_buffer_clear (logbook)) return DC_STATUS_NOMEMORY; - // For devices without a logbook ringbuffer, downloading dives isn't - // possible. This is not considered a fatal error, but handled as if there - // are no dives present. - if (layout->rb_logbook_begin == layout->rb_logbook_end) { - return DC_STATUS_SUCCESS; - } - - // Read the pointer data. - unsigned char pointers[PAGESIZE] = {0}; - rc = dc_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers)); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory page."); - return rc; - } - - // Get the logbook pointers. - unsigned int rb_logbook_first = array_uint16_le (pointers + 4); - unsigned int rb_logbook_last = array_uint16_le (pointers + 6); - if (rb_logbook_last < layout->rb_logbook_begin || - rb_logbook_last >= layout->rb_logbook_end) + // Validate the logbook pointers. + unsigned int rb_logbook_begin = begin; + unsigned int rb_logbook_end = end; + if (rb_logbook_begin < layout->rb_logbook_begin || + rb_logbook_begin > layout->rb_logbook_end) { - ERROR (abstract->context, "Invalid logbook end pointer detected (0x%04x).", rb_logbook_last); - return DC_STATUS_DATAFORMAT; + ERROR (abstract->context, "Invalid logbook begin pointer detected (0x%04x).", rb_logbook_begin); + if (layout->rb_logbook_direction == 0) { + return DC_STATUS_DATAFORMAT; + } + // Fall back to downloading the entire logbook ringbuffer as + // workaround for an invalid logbook begin pointer! + rb_logbook_begin = rb_logbook_end; } - - // Calculate the end pointer. - unsigned int rb_logbook_end = 0; - if (layout->pt_mode_global == 0) { - rb_logbook_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout); - } else { - rb_logbook_end = rb_logbook_last; + if (rb_logbook_end < layout->rb_logbook_begin || + rb_logbook_end > layout->rb_logbook_end) + { + ERROR (abstract->context, "Invalid logbook end pointer detected (0x%04x).", rb_logbook_end); + if (layout->rb_logbook_direction != 0) { + return DC_STATUS_DATAFORMAT; + } + // Fall back to downloading the entire logbook ringbuffer as + // workaround for an invalid logbook end pointer! + rb_logbook_end = rb_logbook_begin; } // Calculate the number of bytes. @@ -295,21 +357,9 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr // full ringbuffer. We always consider the ringbuffer full in that // case, because an empty ringbuffer can be detected by inspecting // the logbook entries once they are downloaded. - unsigned int rb_logbook_size = 0; - if (rb_logbook_first < layout->rb_logbook_begin || - rb_logbook_first >= layout->rb_logbook_end) - { - // Fall back to downloading the entire logbook ringbuffer as - // workaround for an invalid logbook begin pointer! - ERROR (abstract->context, "Invalid logbook begin pointer detected (0x%04x).", rb_logbook_first); - rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin; - } else { - rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_end, layout); - } + unsigned int rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_begin, rb_logbook_end, layout, DC_RINGBUFFER_FULL); // Update and emit a progress event. - progress->current += PAGESIZE; - progress->maximum += PAGESIZE; progress->maximum -= (layout->rb_logbook_end - layout->rb_logbook_begin) - rb_logbook_size; device_event_emit (abstract, DC_EVENT_PROGRESS, progress); @@ -327,7 +377,11 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_logbook_begin, layout->rb_logbook_end, rb_logbook_end); + rc = dc_rbstream_new (&rbstream, abstract, + PAGESIZE, PAGESIZE * device->multipage, + layout->rb_logbook_begin, layout->rb_logbook_end, + layout->rb_logbook_direction ? rb_logbook_end : rb_logbook_begin, + layout->rb_logbook_direction ? DC_RBSTREAM_BACKWARD : DC_RBSTREAM_FORWARD); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); return rc; @@ -403,9 +457,6 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr const oceanic_common_layout_t *layout = device->layout; - // Get the pagesize - unsigned int pagesize = layout->highmem ? 16 * PAGESIZE : PAGESIZE; - // Cache the logbook pointer and size. const unsigned char *logbooks = dc_buffer_get_data (logbook); unsigned int rb_logbook_size = dc_buffer_get_size (logbook); @@ -413,6 +464,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr // Go through the logbook entries a first time, to get the end of // profile pointer and calculate the total amount of bytes in the // profile ringbuffer. + unsigned int rb_profile_begin = INVALID; unsigned int rb_profile_end = INVALID; unsigned int rb_profile_size = 0; @@ -434,22 +486,20 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr } // Get the profile pointers. - unsigned int rb_entry_first = get_profile_first (logbooks + entry, layout, pagesize); - unsigned int rb_entry_last = get_profile_last (logbooks + entry, layout, pagesize); - if (rb_entry_first < layout->rb_profile_begin || - rb_entry_first >= layout->rb_profile_end || - rb_entry_last < layout->rb_profile_begin || - rb_entry_last >= layout->rb_profile_end) + unsigned int rb_entry_begin = 0, rb_entry_end = 0; + oceanic_common_device_get_profile (logbooks + entry, layout, &rb_entry_begin, &rb_entry_end); + if (rb_entry_begin < layout->rb_profile_begin || + rb_entry_begin > layout->rb_profile_end || + rb_entry_end < layout->rb_profile_begin || + rb_entry_end > layout->rb_profile_end) { ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%06x 0x%06x).", - rb_entry_first, rb_entry_last); + rb_entry_begin, rb_entry_end); status = DC_STATUS_DATAFORMAT; continue; } - // Calculate the end pointer and the number of bytes. - unsigned int rb_entry_end = RB_PROFILE_INCR (rb_entry_last, pagesize, layout); - unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_first, rb_entry_last, layout) + pagesize; + DEBUG (abstract->context, "Entry: %08x %08x", rb_entry_begin, rb_entry_end); // Take the end pointer of the most recent logbook entry as the // end of profile pointer. @@ -457,11 +507,13 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr rb_profile_end = previous = rb_entry_end; } + // Calculate the number of bytes. + unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_begin, rb_entry_end, layout, DC_RINGBUFFER_FULL); + // Skip gaps between the profiles. - unsigned int gap = 0; - if (rb_entry_end != previous) { - WARNING (abstract->context, "Profiles are not continuous."); - gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout); + unsigned int gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout, DC_RINGBUFFER_EMPTY); + if (gap) { + WARNING (abstract->context, "Profiles are not continuous (%u bytes).", gap); } // Make sure the profile size is valid. @@ -470,13 +522,18 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr break; } + // Update the profile begin pointer. + rb_profile_begin = rb_entry_begin; + // Update the total profile size. rb_profile_size += rb_entry_size + gap; remaining -= rb_entry_size + gap; - previous = rb_entry_first; + previous = rb_entry_begin; } + DEBUG (abstract->context, "Profile: %08x %08x", rb_profile_begin, rb_profile_end); + // At this point, we know the exact amount of data // that needs to be transfered for the profiles. progress->maximum -= (layout->rb_profile_end - layout->rb_profile_begin) - rb_profile_size; @@ -489,7 +546,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_profile_begin, layout->rb_profile_end, rb_profile_end); + rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_profile_begin, layout->rb_profile_end, rb_profile_end, DC_RBSTREAM_BACKWARD); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); return rc; @@ -524,28 +581,28 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr } // Get the profile pointers. - unsigned int rb_entry_first = get_profile_first (logbooks + entry, layout, pagesize); - unsigned int rb_entry_last = get_profile_last (logbooks + entry, layout, pagesize); - if (rb_entry_first < layout->rb_profile_begin || - rb_entry_first >= layout->rb_profile_end || - rb_entry_last < layout->rb_profile_begin || - rb_entry_last >= layout->rb_profile_end) + unsigned int rb_entry_begin = 0, rb_entry_end = 0; + oceanic_common_device_get_profile (logbooks + entry, layout, &rb_entry_begin, &rb_entry_end); + if (rb_entry_begin < layout->rb_profile_begin || + rb_entry_begin > layout->rb_profile_end || + rb_entry_end < layout->rb_profile_begin || + rb_entry_end > layout->rb_profile_end) { ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%06x 0x%06x).", - rb_entry_first, rb_entry_last); + rb_entry_begin, rb_entry_end); status = DC_STATUS_DATAFORMAT; continue; } - // Calculate the end pointer and the number of bytes. - unsigned int rb_entry_end = RB_PROFILE_INCR (rb_entry_last, pagesize, layout); - unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_first, rb_entry_last, layout) + pagesize; + DEBUG (abstract->context, "Entry: %08x %08x", rb_entry_begin, rb_entry_end); + + // Calculate the number of bytes. + unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_begin, rb_entry_end, layout, DC_RINGBUFFER_FULL); // Skip gaps between the profiles. - unsigned int gap = 0; - if (rb_entry_end != previous) { - WARNING (abstract->context, "Profiles are not continuous."); - gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout); + unsigned int gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout, DC_RINGBUFFER_EMPTY); + if (gap) { + WARNING (abstract->context, "Profiles are not continuous (%u bytes).", gap); } // Make sure the profile size is valid. @@ -566,7 +623,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr } remaining -= rb_entry_size + gap; - previous = rb_entry_first; + previous = rb_entry_begin; // Prepend the logbook entry to the profile data. The memory buffer is // large enough to store this entry. @@ -604,6 +661,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr dc_status_t oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { + dc_status_t rc = DC_STATUS_SUCCESS; oceanic_common_device_t *device = (oceanic_common_device_t *) abstract; assert (device != NULL); @@ -611,45 +669,37 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac const oceanic_common_layout_t *layout = device->layout; + // For devices without a logbook and profile ringbuffer, downloading dives + // isn't possible. This is not considered a fatal error, but handled as if + // there are no dives present. + if (layout->rb_logbook_begin == layout->rb_logbook_end && + layout->rb_profile_begin == layout->rb_profile_end) { + return DC_STATUS_SUCCESS; + } + // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; - progress.maximum = PAGESIZE + + progress.maximum = (layout->rb_logbook_end - layout->rb_logbook_begin) + (layout->rb_profile_end - layout->rb_profile_begin); device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); - // Emit a vendor event. - dc_event_vendor_t vendor; - vendor.data = device->version; - vendor.size = sizeof (device->version); - device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - - // Read the device id. - unsigned char id[PAGESIZE] = {0}; - dc_status_t rc = dc_device_read (abstract, layout->cf_devinfo, id, sizeof (id)); + // Read the device info. + rc = VTABLE(abstract)->devinfo (abstract, &progress); if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory page."); return rc; } - // Update and emit a progress event. - progress.current += PAGESIZE; - device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + // Read the ringbuffer pointers. + unsigned int rb_logbook_begin = 0, rb_logbook_end = 0; + unsigned int rb_profile_begin = 0, rb_profile_end = 0; + rc = VTABLE(abstract)->pointers (abstract, &progress, &rb_logbook_begin, &rb_logbook_end, &rb_profile_begin, &rb_profile_end); + if (rc != DC_STATUS_SUCCESS) { + return rc; + } - // Emit a device info event. - dc_event_devinfo_t devinfo; - devinfo.model = array_uint16_be (id + 8); - devinfo.firmware = device->firmware; - if (layout->pt_mode_serial == 0) - devinfo.serial = array_convert_bcd2dec (id + 10, 3); - else if (layout->pt_mode_serial == 1) - devinfo.serial = array_convert_bin2dec (id + 11, 3); - else - devinfo.serial = - (id[11] & 0x0F) * 100000 + ((id[11] & 0xF0) >> 4) * 10000 + - (id[12] & 0x0F) * 1000 + ((id[12] & 0xF0) >> 4) * 100 + - (id[13] & 0x0F) * 10 + ((id[13] & 0xF0) >> 4) * 1; - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + DEBUG (abstract->context, "Logbook: %08x %08x", rb_logbook_begin, rb_logbook_end); + DEBUG (abstract->context, "Profile: %08x %08x", rb_profile_begin, rb_profile_end); // Memory buffer for the logbook data. dc_buffer_t *logbook = dc_buffer_new (0); @@ -658,7 +708,7 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac } // Download the logbook ringbuffer. - rc = VTABLE(abstract)->logbook (abstract, &progress, logbook); + rc = VTABLE(abstract)->logbook (abstract, &progress, logbook, rb_logbook_begin, rb_logbook_end); if (rc != DC_STATUS_SUCCESS) { dc_buffer_free (logbook); return rc; diff --git a/src/oceanic_common.h b/src/oceanic_common.h index 045dca4..43254a9 100644 --- a/src/oceanic_common.h +++ b/src/oceanic_common.h @@ -125,8 +125,12 @@ extern "C" { #define I200CV2 0x4749 #define GEOAIR 0x474B +// i330r +#define DSX 0x4741 +#define I330R 0x4744 + #define PAGESIZE 0x10 -#define FPMAXSIZE 0x20 +#define FPMAXSIZE 0x200 #define OCEANIC_COMMON_MATCH(version,patterns,firmware) \ oceanic_common_match ((version), (patterns), \ @@ -144,6 +148,7 @@ typedef struct oceanic_common_layout_t { unsigned int rb_logbook_begin; unsigned int rb_logbook_end; unsigned int rb_logbook_entry_size; + unsigned int rb_logbook_direction; // Profile ringbuffer unsigned int rb_profile_begin; unsigned int rb_profile_end; @@ -168,7 +173,9 @@ typedef struct oceanic_common_device_t { typedef struct oceanic_common_device_vtable_t { dc_device_vtable_t base; - dc_status_t (*logbook) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook); + dc_status_t (*devinfo) (dc_device_t *device, dc_event_progress_t *progress); + dc_status_t (*pointers) (dc_device_t *device, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end); + dc_status_t (*logbook) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end); dc_status_t (*profile) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata); } oceanic_common_device_vtable_t; @@ -186,7 +193,15 @@ void oceanic_common_device_init (oceanic_common_device_t *device); dc_status_t -oceanic_common_device_logbook (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook); +oceanic_common_device_devinfo (dc_device_t *device, dc_event_progress_t *progress); + +dc_status_t +oceanic_common_device_pointers (dc_device_t *device, dc_event_progress_t *progress, + unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, + unsigned int *rb_profile_begin, unsigned int *rb_profile_end); + +dc_status_t +oceanic_common_device_logbook (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end); dc_status_t oceanic_common_device_profile (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata); diff --git a/src/parser.c b/src/parser.c index d63c5c6..bc143f4 100644 --- a/src/parser.c +++ b/src/parser.c @@ -66,9 +66,6 @@ #include "oceans_s1.h" #include "divesoft_freedom.h" -// Not merged upstream yet -#include "garmin.h" - #include "context-private.h" #include "parser-private.h" #include "device-private.h" @@ -76,7 +73,7 @@ #define REACTPROWHITE 0x4354 static dc_status_t -dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, dc_family_t family, unsigned int model, unsigned int serial) +dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, dc_family_t family, unsigned int model) { dc_status_t rc = DC_STATUS_SUCCESS; dc_parser_t *parser = NULL; @@ -95,11 +92,11 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned if (model == 0x01) rc = suunto_eon_parser_create (&parser, context, data, size, 1); else - rc = suunto_vyper_parser_create (&parser, context, data, size, serial); + rc = suunto_vyper_parser_create (&parser, context, data, size); break; case DC_FAMILY_SUUNTO_VYPER2: case DC_FAMILY_SUUNTO_D9: - rc = suunto_d9_parser_create (&parser, context, data, size, model, serial); + rc = suunto_d9_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_SUUNTO_EONSTEEL: rc = suunto_eonsteel_parser_create(&parser, context, data, size, model); @@ -127,10 +124,11 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned rc = oceanic_veo250_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_OCEANIC_ATOM2: + case DC_FAMILY_PELAGIC_I330R: if (model == REACTPROWHITE) rc = oceanic_veo250_parser_create (&parser, context, data, size, model); else - rc = oceanic_atom2_parser_create (&parser, context, data, size, model, serial); + rc = oceanic_atom2_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_MARES_NEMO: case DC_FAMILY_MARES_PUCK: @@ -140,14 +138,14 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned rc = mares_darwin_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_MARES_ICONHD: - rc = mares_iconhd_parser_create (&parser, context, data, size, model, serial); + rc = mares_iconhd_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_HW_OSTC: - rc = hw_ostc_parser_create (&parser, context, data, size, serial); + rc = hw_ostc_parser_create (&parser, context, data, size); break; case DC_FAMILY_HW_FROG: case DC_FAMILY_HW_OSTC3: - rc = hw_ostc3_parser_create (&parser, context, data, size, model, serial); + rc = hw_ostc3_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_CRESSI_EDY: case DC_FAMILY_ZEAGLE_N2ITION3: @@ -163,10 +161,10 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned rc = atomics_cobalt_parser_create (&parser, context, data, size); break; case DC_FAMILY_SHEARWATER_PREDATOR: - rc = shearwater_predator_parser_create (&parser, context, data, size, model, serial); + rc = shearwater_predator_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_SHEARWATER_PETREL: - rc = shearwater_petrel_parser_create (&parser, context, data, size, model, serial); + rc = shearwater_petrel_parser_create (&parser, context, data, size, model); break; case DC_FAMILY_DIVERITE_NITEKQ: rc = diverite_nitekq_parser_create (&parser, context, data, size); @@ -209,11 +207,6 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned break; default: return DC_STATUS_INVALIDARGS; - - // Not merged upstream yet - case DC_FAMILY_GARMIN: - rc = garmin_parser_create (&parser, context, data, size); - break; } *out = parser; @@ -231,7 +224,7 @@ dc_parser_new (dc_parser_t **out, dc_device_t *device, const unsigned char data[ return DC_STATUS_INVALIDARGS; status = dc_parser_new_internal (&parser, device->context, data, size, - dc_device_get_type (device), device->devinfo.model, device->devinfo.serial); + dc_device_get_type (device), device->devinfo.model); if (status != DC_STATUS_SUCCESS) goto error_exit; @@ -253,7 +246,7 @@ dc_status_t dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descriptor, const unsigned char data[], size_t size) { return dc_parser_new_internal (out, context, data, size, - dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor), 0); + dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor)); } dc_parser_t * diff --git a/src/pelagic_i330r.c b/src/pelagic_i330r.c new file mode 100644 index 0000000..17aad3b --- /dev/null +++ b/src/pelagic_i330r.c @@ -0,0 +1,646 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Janice McLaughlin + * + * 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 // memcpy +#include // malloc, free +#include + +#include + +#include "pelagic_i330r.h" +#include "oceanic_common.h" + +#include "context-private.h" +#include "device-private.h" +#include "ringbuffer.h" +#include "rbstream.h" +#include "checksum.h" +#include "array.h" + +#define UNDEFINED 0 + +#define STARTBYTE 0xCD + +#define FLAG_NONE 0x00 +#define FLAG_REQUEST 0x40 +#define FLAG_DATA 0x80 +#define FLAG_LAST 0xC0 + +#define CMD_ACCESS_REQUEST 0xFA +#define CMD_ACCESS_CODE 0xFB +#define CMD_AUTHENTICATION 0x97 +#define CMD_WAKEUP_RDONLY 0x21 +#define CMD_WAKEUP_RDWR 0x22 +#define CMD_READ_HW_CAL 0x27 +#define CMD_READ_A2D 0x25 +#define CMD_READ_DEVICE_REC 0x31 +#define CMD_READ_GEN_SET 0x29 +#define CMD_READ_EXFLASHMAP 0x2F +#define CMD_READ_FLASH 0x0D + +#define RSP_READY 1 +#define RSP_DONE 2 + +#define MAXPACKET 255 + +#define MAXPASSCODE 6 + +typedef struct pelagic_i330r_device_t { + oceanic_common_device_t base; + dc_iostream_t *iostream; + unsigned char accesscode[16]; + unsigned char id[16]; + unsigned char hwcal[256]; + unsigned char flashmap[256]; + unsigned int model; +} pelagic_i330r_device_t; + +static dc_status_t pelagic_i330r_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); +static dc_status_t pelagic_i330r_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progress); +static dc_status_t pelagic_i330r_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end); + +static const oceanic_common_device_vtable_t pelagic_i330r_device_vtable = { + { + sizeof(pelagic_i330r_device_t), + DC_FAMILY_PELAGIC_I330R, + oceanic_common_device_set_fingerprint, /* set_fingerprint */ + pelagic_i330r_device_read, /* read */ + NULL, /* write */ + oceanic_common_device_dump, /* dump */ + oceanic_common_device_foreach, /* foreach */ + NULL, /* timesync */ + NULL /* close */ + }, + pelagic_i330r_device_devinfo, + pelagic_i330r_device_pointers, + oceanic_common_device_logbook, + oceanic_common_device_profile, +}; + +static const oceanic_common_layout_t pelagic_i330r = { + 0x00400000, /* memsize */ + 0, /* highmem */ + UNDEFINED, /* cf_devinfo */ + UNDEFINED, /* cf_pointers */ + 0x00102000, /* rb_logbook_begin */ + 0x00106000, /* rb_logbook_end */ + 64, /* rb_logbook_entry_size */ + 0, /* rb_logbook_direction */ + 0x0010A000, /* rb_profile_begin */ + 0x00400000, /* rb_profile_end */ + 1, /* pt_mode_global */ + 4, /* pt_mode_logbook */ + UNDEFINED, /* pt_mode_serial */ +}; + +static const oceanic_common_layout_t pelagic_dsx = { + 0x02000000, /* memsize */ + 0, /* highmem */ + UNDEFINED, /* cf_devinfo */ + UNDEFINED, /* cf_pointers */ + 0x00800000, /* rb_logbook_begin */ + 0x00880000, /* rb_logbook_end */ + 512, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ + 0x01000000, /* rb_profile_begin */ + 0x02000000, /* rb_profile_end */ + 1, /* pt_mode_global */ + 4, /* pt_mode_logbook */ + UNDEFINED /* pt_mode_serial */ +}; + +static unsigned char +checksum (const unsigned char data[], unsigned int size) +{ + unsigned int csum = 0; + for (unsigned int i = 0; i < size; i++) { + unsigned int a = csum ^ data[i]; + unsigned int b = (a >> 7) ^ ((a >> 4) ^ a); + csum = ((b << 4) & 0xFF) ^ ((b << 1) & 0xFF); + } + return csum & 0xFF; +} + +static dc_status_t +pelagic_i330r_send (pelagic_i330r_device_t *device, unsigned char cmd, unsigned char flag, const unsigned char data[], unsigned int size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + if (size > MAXPACKET) { + ERROR (abstract->context, "Packet payload is too large (%u).", size); + return DC_STATUS_INVALIDARGS; + } + + unsigned char packet[MAXPACKET + 5] = { + STARTBYTE, + flag, + cmd, + 0, + size + }; + if (size) { + memcpy(packet + 5, data, size); + } + packet[3] = checksum (packet, size + 5); + + // Send the data packet. + status = dc_iostream_write (device->iostream, packet, size + 5, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +pelagic_i330r_recv (pelagic_i330r_device_t *device, unsigned char cmd, unsigned char data[], unsigned int size, unsigned int *errorcode) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[MAXPACKET + 5] = {0}; + unsigned int errcode = 0; + + unsigned int nbytes = 0; + while (1) { + // Read the data packet. + size_t transferred = 0; + status = dc_iostream_read (device->iostream, packet, sizeof(packet), &transferred); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the data packet."); + return status; + } + + // Verify the minimum packet size. + if (transferred < 5) { + ERROR (abstract->context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred); + return DC_STATUS_PROTOCOL; + } + + // Verify the start byte. + if (packet[0] != STARTBYTE) { + ERROR (abstract->context, "Unexpected packet start byte (%02x).", packet[0]); + return DC_STATUS_PROTOCOL; + } + + // Verify the command byte. + if (packet[2] != cmd) { + ERROR (abstract->context, "Unexpected packet command byte (%02x).", packet[2]); + return DC_STATUS_PROTOCOL; + } + + // Verify the length byte. + unsigned int length = packet[4]; + if (length + 5 > transferred) { + ERROR (abstract->context, "Invalid packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + // Verify the checksum. + unsigned char crc = packet[3]; packet[3] = 0; + unsigned char ccrc = checksum (packet, length + 5); + if (crc != ccrc) { + ERROR (abstract->context, "Unexpected packet checksum (%02x %02x).", crc, ccrc); + return DC_STATUS_PROTOCOL; + } + + // Check the flag byte for the last packet. + unsigned char flag = packet[1]; + if ((flag & FLAG_LAST) == FLAG_LAST) { + // The last packet (typically 2 bytes) does not get appended! + if (length) { + errcode = packet[5]; + } + break; + } + + // Append the payload data to the output buffer. If the output + // buffer is too small, the error is not reported immediately + // but delayed until all packets have been received. + if (nbytes < size) { + unsigned int n = length; + if (nbytes + n > size) { + n = size - nbytes; + } + memcpy (data + nbytes, packet + 5, n); + } + nbytes += length; + } + + // Verify the expected number of bytes. + if (nbytes != size) { + ERROR (abstract->context, "Unexpected number of bytes received (%u %u).", nbytes, size); + return DC_STATUS_PROTOCOL; + } + + if (errorcode) { + *errorcode = errcode; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +pelagic_i330r_transfer (pelagic_i330r_device_t *device, unsigned char cmd, unsigned char flag, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int response) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned int errorcode = 0; + + status = pelagic_i330r_send (device, cmd, flag, data, size); + if (status != DC_STATUS_SUCCESS) + return status; + + status = pelagic_i330r_recv (device, cmd, answer, asize, &errorcode); + if (status != DC_STATUS_SUCCESS) + return status; + + if (errorcode != response) { + ERROR (abstract->context, "Unexpected response code (%u)", errorcode); + return DC_STATUS_PROTOCOL; + } + + return status; +} + +static dc_status_t +pelagic_i330r_init_accesscode (pelagic_i330r_device_t *device) +{ + dc_status_t status = DC_STATUS_SUCCESS; + + const unsigned char zero[9] = {0}; + status = pelagic_i330r_transfer (device, CMD_ACCESS_REQUEST, FLAG_REQUEST, zero, sizeof(zero), NULL, 0, RSP_READY); + if (status != DC_STATUS_SUCCESS) + return status; + + status = pelagic_i330r_transfer (device, CMD_ACCESS_REQUEST, FLAG_DATA, device->accesscode, sizeof(device->accesscode), NULL, 0, RSP_DONE); + if (status != DC_STATUS_SUCCESS) + return status; + + return status; +} + +static dc_status_t +pelagic_i330r_init_passcode (pelagic_i330r_device_t *device, const char *pincode) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char passcode[MAXPASSCODE] = {0}; + + // Check the maximum length. + size_t len = pincode ? strlen (pincode) : 0; + if (len > sizeof(passcode)) { + ERROR (abstract->context, "Invalid pincode length (" DC_PRINTF_SIZE ").", len); + return DC_STATUS_INVALIDARGS; + } + + // Convert to binary number. + unsigned int offset = sizeof(passcode) - len; + for (unsigned int i = 0; i < len; i++) { + unsigned char c = pincode[i]; + if (c < '0' || c > '9') { + ERROR (abstract->context, "Invalid pincode character (%c).", c); + return DC_STATUS_INVALIDARGS; + } + passcode[offset + i] = c - '0'; + } + + const unsigned char zero[9] = {0}; + status = pelagic_i330r_transfer (device, CMD_ACCESS_CODE, FLAG_REQUEST, zero, sizeof(zero), NULL, 0, RSP_READY); + if (status != DC_STATUS_SUCCESS) + return status; + + status = pelagic_i330r_transfer (device, CMD_ACCESS_CODE, FLAG_DATA, passcode, sizeof(passcode), device->accesscode, sizeof(device->accesscode), RSP_DONE); + if (status != DC_STATUS_SUCCESS) + return status; + + HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Access code", device->accesscode, sizeof(device->accesscode)); + + return status; +} + +static dc_status_t +pelagic_i330r_init_handshake (pelagic_i330r_device_t *device, unsigned int readwrite) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + const unsigned char cmd = readwrite ? CMD_WAKEUP_RDWR : CMD_WAKEUP_RDONLY; + + const unsigned char args[9] = {0, 0, 0, 0, 0x0C, 0, 0, 0, 0}; + status = pelagic_i330r_transfer (device, cmd, FLAG_REQUEST, args, sizeof(args), device->id, sizeof(device->id), RSP_DONE); + if (status != DC_STATUS_SUCCESS) + return status; + + HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "ID", device->id, sizeof(device->id)); + + device->model = array_uint16_be (device->id + 12); + + return status; +} + +static dc_status_t +pelagic_i330r_init_auth (pelagic_i330r_device_t *device) +{ + dc_status_t status = DC_STATUS_SUCCESS; + + const unsigned char args[2][9] = { + {0xFF, 0xFF, 0xFF, 0xFF}, // DSX + {0x37, 0x30, 0x31, 0x55}, // I330R + }; + unsigned int args_idx = device->model == DSX ? 0 : 1; + status = pelagic_i330r_transfer (device, CMD_AUTHENTICATION, FLAG_REQUEST, args[args_idx], sizeof(args[args_idx]), NULL, 0, RSP_READY); + if (status != DC_STATUS_SUCCESS) + return status; + + return status; +} + +static dc_status_t +pelagic_i330r_init (pelagic_i330r_device_t *device) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + // Get the bluetooth access code. + status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_GET_ACCESSCODE, device->accesscode, sizeof(device->accesscode)); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR (abstract->context, "Failed to get the access code."); + return status; + } + + if (array_isequal (device->accesscode, sizeof(device->accesscode), 0)) { + // Request to display the PIN code. + status = pelagic_i330r_init_accesscode (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to display the PIN code."); + return status; + } + + // Get the bluetooth PIN code. + char pincode[6 + 1] = {0}; + status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_GET_PINCODE, pincode, sizeof(pincode)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to get the PIN code."); + return status; + } + + // Force a null terminated string. + pincode[sizeof(pincode) - 1] = 0; + + // Request the access code. + status = pelagic_i330r_init_passcode (device, pincode); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to request the access code."); + return status; + } + + // Store the bluetooth access code. + status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_SET_ACCESSCODE, device->accesscode, sizeof(device->accesscode)); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR (abstract->context, "Failed to store the access code."); + return status; + } + } + + // Request access. + status = pelagic_i330r_init_accesscode (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to request access."); + return status; + } + + // Send the wakeup command. + status = pelagic_i330r_init_handshake (device, 1); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the wakeup command."); + return status; + } + + // Send the authentication code. + status = pelagic_i330r_init_auth (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the authentication code."); + return status; + } + + return status; +} + +static dc_status_t +pelagic_i330r_download (pelagic_i330r_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + status = pelagic_i330r_transfer (device, cmd, FLAG_REQUEST, data, size, answer, asize, RSP_DONE); + if (status != DC_STATUS_SUCCESS) + return status; + + // Verify the checksum + unsigned short crc = array_uint16_be (answer + asize - 2); + unsigned short ccrc = checksum_crc16_ccitt (answer, asize - 2, 0xffff, 0x0000); + if (crc != ccrc) { + ERROR (abstract->context, "Unexpected data checksum (%04x %04x).", crc, ccrc); + return DC_STATUS_PROTOCOL; + } + + return status; +} + +dc_status_t +pelagic_i330r_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) +{ + dc_status_t status = DC_STATUS_SUCCESS; + pelagic_i330r_device_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (pelagic_i330r_device_t *) dc_device_allocate (context, &pelagic_i330r_device_vtable.base); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Initialize the base class. + oceanic_common_device_init (&device->base); + + // Override the base class values. + device->base.multipage = 256; + + // Set the default values. + device->iostream = iostream; + memset (device->accesscode, 0, sizeof(device->accesscode)); + memset (device->id, 0, sizeof(device->id)); + memset (device->hwcal, 0, sizeof(device->hwcal)); + memset (device->flashmap, 0, sizeof(device->flashmap)); + device->model = 0; + + // Set the timeout for receiving data (3000 ms). + status = dc_iostream_set_timeout (device->iostream, 3000); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the timeout."); + goto error_free; + } + + // Perform the bluetooth authentication. + status = pelagic_i330r_init (device); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to perform the bluetooth authentication."); + goto error_free; + } + + // Download the calibration data. + const unsigned char args[9] = {0, 0, 0, 0, 0, 0x01, 0, 0, 0}; + status = pelagic_i330r_download (device, CMD_READ_HW_CAL, args, sizeof(args), device->hwcal, sizeof(device->hwcal)); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to download the calibration data."); + goto error_free; + } + + HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Hwcal", device->hwcal, sizeof(device->hwcal)); + + // Download the flash map. + const unsigned char zero[9] = {0}; + status = pelagic_i330r_download (device, CMD_READ_EXFLASHMAP, zero, sizeof(zero), device->flashmap, sizeof(device->flashmap)); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to download the flash map."); + goto error_free; + } + + HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Flashmap", device->flashmap, sizeof(device->flashmap)); + + // Detect the memory layout. + if (device->model == DSX) { + device->base.layout = &pelagic_dsx; + } else { + device->base.layout = &pelagic_i330r; + } + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; + +error_free: + dc_device_deallocate ((dc_device_t *) device); + return status; +} + +static dc_status_t +pelagic_i330r_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + pelagic_i330r_device_t *device = (pelagic_i330r_device_t*) abstract; + + unsigned char command[9] = {0}; + array_uint32_le_set(command + 0, address); + array_uint32_le_set(command + 4, size); + + status = pelagic_i330r_transfer (device, CMD_READ_FLASH, FLAG_NONE, command, sizeof(command), data, size, RSP_DONE); + if (status != DC_STATUS_SUCCESS) { + return status; + } + + return status; +} + +static dc_status_t +pelagic_i330r_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progress) +{ + pelagic_i330r_device_t *device = (pelagic_i330r_device_t *) abstract; + + assert (device != NULL); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = + bcd2dec (device->hwcal[12]) + + bcd2dec (device->hwcal[13]) * 100 + + bcd2dec (device->hwcal[14]) * 10000; + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +pelagic_i330r_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end) +{ + pelagic_i330r_device_t *device = (pelagic_i330r_device_t *) abstract; + + assert (device != NULL); + assert (device->base.layout != NULL); + assert (rb_logbook_begin != NULL && rb_logbook_end != NULL); + assert (rb_profile_begin != NULL && rb_profile_end != NULL); + + const oceanic_common_layout_t *layout = device->base.layout; + + // Get the logbook pointers. + unsigned int rb_logbook_min = array_uint32_le (device->flashmap + 0x50); + unsigned int rb_logbook_max = array_uint32_le (device->flashmap + 0x54); + unsigned int rb_logbook_first = array_uint32_le (device->flashmap + 0x58); + unsigned int rb_logbook_last = array_uint32_le (device->flashmap + 0x5C); + if (rb_logbook_min != 0 && rb_logbook_max != 0) { + rb_logbook_max += 1; + } + + // Get the profile pointers. + unsigned int rb_profile_min = array_uint32_le (device->flashmap + 0x70); + unsigned int rb_profile_max = array_uint32_le (device->flashmap + 0x74); + unsigned int rb_profile_first = array_uint32_le (device->flashmap + 0x78); + unsigned int rb_profile_last = array_uint32_le (device->flashmap + 0x7C); + if (rb_profile_min != 0 && rb_profile_max != 0) { + rb_profile_max += 1; + } + + // Check the logbook ringbuffer area. + if (rb_logbook_min != layout->rb_logbook_begin || + rb_logbook_max != layout->rb_logbook_end) { + ERROR (abstract->context, "Unexpected logbook ringbuffer area (%08x %08x)", + rb_logbook_min, rb_logbook_max); + return DC_STATUS_DATAFORMAT; + } + + // Check the profile ringbuffer area. + if (rb_profile_min != layout->rb_profile_begin || + rb_profile_max != layout->rb_profile_end) { + ERROR (abstract->context, "Unexpected profile ringbuffer area (%08x %08x)", + rb_profile_min, rb_profile_max); + return DC_STATUS_DATAFORMAT; + } + + // Get the begin/end pointers. + if (device->model == DSX) { + *rb_logbook_begin = rb_logbook_first; + *rb_logbook_end = rb_logbook_last; + } else { + *rb_logbook_begin = rb_logbook_min; + *rb_logbook_end = rb_logbook_last + 1; + } + *rb_profile_begin = rb_profile_first; + *rb_profile_end = rb_profile_last; + + return DC_STATUS_SUCCESS; +} diff --git a/src/pelagic_i330r.h b/src/pelagic_i330r.h new file mode 100644 index 0000000..23045cd --- /dev/null +++ b/src/pelagic_i330r.h @@ -0,0 +1,40 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Janice McLaughlin + * + * 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 PELAGIC_I330R_H +#define PELAGIC_I330R_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +pelagic_i330r_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PELAGIC_I330R_H */