diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 86ef1162..362bfd70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: CC: ${{ matrix.compiler }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: sudo apt-get install libbluetooth-dev libusb-1.0-0-dev - run: autoreconf --install --force @@ -30,7 +30,7 @@ jobs: run: | make install DESTDIR=$PWD/artifacts tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ github.job }}-${{ matrix.compiler }} path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz @@ -50,7 +50,7 @@ jobs: CC: ${{ matrix.compiler }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: brew install autoconf automake libtool hidapi libusb - run: autoreconf --install --force @@ -61,7 +61,7 @@ jobs: run: | make install DESTDIR=$PWD/artifacts tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ github.job }}-${{ matrix.compiler }} path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz @@ -78,7 +78,7 @@ jobs: arch: [i686, x86_64] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools - name: Install libusb @@ -118,7 +118,7 @@ jobs: run: | make install DESTDIR=$PWD/artifacts tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ github.job }}-${{ matrix.arch }} path: ${{ github.job }}-${{ matrix.arch }}.tar.gz @@ -161,13 +161,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | autoreconf --install --force ./configure --prefix=/usr make -C src revision.h - run: $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ github.job }} path: contrib/android/libs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fcf8f23b..bc4773b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: name: Release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Version number id: version diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 43f54733..5c1c6f93 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -71,6 +71,7 @@ LOCAL_SRC_FILES := \ src/oceans_s1_parser.c \ src/packet.c \ src/parser.c \ + src/pelagic_i330r.c \ src/platform.c \ src/rbstream.c \ src/reefnet_sensus.c \ diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index 704ef8b7..991f1ac8 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -239,6 +239,7 @@ + @@ -357,6 +358,7 @@ + diff --git a/examples/common.c b/examples/common.c index f30337ba..4f965f16 100644 --- a/examples/common.c +++ b/examples/common.c @@ -72,6 +72,7 @@ static const backend_table_t g_backends[] = { {"vtpro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, {"veo250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, {"atom2", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, + {"i330r", DC_FAMILY_PELAGIC_I330R, 0x4744}, {"nemo", DC_FAMILY_MARES_NEMO, 0}, {"puck", DC_FAMILY_MARES_PUCK, 7}, {"darwin", DC_FAMILY_MARES_DARWIN, 0}, diff --git a/examples/output_xml.c b/examples/output_xml.c index 1bb673bf..0f1621d1 100644 --- a/examples/output_xml.c +++ b/examples/output_xml.c @@ -489,7 +489,27 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u break; fprintf (output->ostream, "\n", str.desc, str.value); + } + // Parse the GPS location. + message ("Parsing the GPS location.\n"); + dc_location_t location = {0}; + status = dc_parser_get_field (parser, DC_FIELD_LOCATION, 0, &location); + if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) { + ERROR ("Error parsing the GPS location."); + goto cleanup; + } + + if (status != DC_STATUS_UNSUPPORTED) { + fprintf (output->ostream, + "\n" + " %.6f\n" + " %.6f\n" + " %.2f\n" + "\n", + location.latitude, + location.longitude, + convert_depth(location.altitude, output->units)); } // Parse the sample data. diff --git a/include/libdivecomputer/ble.h b/include/libdivecomputer/ble.h index 1452873a..a13fe87c 100644 --- a/include/libdivecomputer/ble.h +++ b/include/libdivecomputer/ble.h @@ -33,6 +33,21 @@ extern "C" { */ #define DC_IOCTL_BLE_GET_NAME DC_IOCTL_IOR('b', 0, DC_IOCTL_SIZE_VARIABLE) +/** + * Get the bluetooth authentication PIN code. + * + * The data format is a NULL terminated string. + */ +#define DC_IOCTL_BLE_GET_PINCODE DC_IOCTL_IOR('b', 1, DC_IOCTL_SIZE_VARIABLE) + +/** + * Get/set the bluetooth authentication access code. + * + * The data format is a variable sized byte array. + */ +#define DC_IOCTL_BLE_GET_ACCESSCODE DC_IOCTL_IOR('b', 2, DC_IOCTL_SIZE_VARIABLE) +#define DC_IOCTL_BLE_SET_ACCESSCODE DC_IOCTL_IOW('b', 2, DC_IOCTL_SIZE_VARIABLE) + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index 0d5ab17d..0ec8a9ff 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -78,6 +78,7 @@ typedef enum dc_family_t { DC_FAMILY_OCEANIC_VTPRO = (4 << 16), DC_FAMILY_OCEANIC_VEO250, DC_FAMILY_OCEANIC_ATOM2, + DC_FAMILY_PELAGIC_I330R, /* Mares */ DC_FAMILY_MARES_NEMO = (5 << 16), DC_FAMILY_MARES_PUCK, diff --git a/include/libdivecomputer/parser.h b/include/libdivecomputer/parser.h index 0d286c84..7964f865 100644 --- a/include/libdivecomputer/parser.h +++ b/include/libdivecomputer/parser.h @@ -69,6 +69,7 @@ typedef enum dc_field_type_t { DC_FIELD_DIVEMODE, DC_FIELD_DECOMODEL, DC_FIELD_STRING, + DC_FIELD_LOCATION, } dc_field_type_t; // Make it easy to test support compile-time with "#ifdef DC_FIELD_STRING" @@ -279,6 +280,18 @@ typedef struct dc_field_string_t { const char *value; } dc_field_string_t; +/* + * GPS Location + * + * The latitude and longitude are in decimal degrees, and the (optional) + * altitude in meters. + */ +typedef struct dc_location_t { + double latitude; + double longitude; + double altitude; +} dc_location_t; + typedef union dc_sample_value_t { unsigned int time; /* Milliseconds */ double depth; diff --git a/src/Makefile.am b/src/Makefile.am index 21399599..495d25e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/cochran_commander.c b/src/cochran_commander.c index 38328aec..83f88009 100644 --- a/src/cochran_commander.c +++ b/src/cochran_commander.c @@ -603,7 +603,7 @@ cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_ // Corrupt dive, guess the end address sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num); - return ringbuffer_distance(sample_start_address, sample_end_address, 0, device->layout->rb_profile_begin, device->layout->rb_profile_end); + return ringbuffer_distance(sample_start_address, sample_end_address, DC_RINGBUFFER_EMPTY, device->layout->rb_profile_begin, device->layout->rb_profile_end); } @@ -965,7 +965,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call last_start_address = base + array_uint32_le(data.config + layout->cf_last_log ); // Create the ringbuffer stream. - status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address); + status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address, DC_RBSTREAM_BACKWARD); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); goto error; diff --git a/src/cressi_edy.c b/src/cressi_edy.c index d61b84e1..c4e78c13 100644 --- a/src/cressi_edy.c +++ b/src/cressi_edy.c @@ -432,7 +432,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v } // Get the number of logbook items. - unsigned int count = ringbuffer_distance (first, last, 0, layout->rb_logbook_begin, layout->rb_logbook_end) + 1; + unsigned int count = ringbuffer_distance (first, last, DC_RINGBUFFER_EMPTY, layout->rb_logbook_begin, layout->rb_logbook_end) + 1; // Get the profile pointer. unsigned int eop = array_uint_le (logbook + layout->config + 2, layout->rb_logbook_size) * SZ_PAGE + layout->rb_profile_begin; @@ -457,7 +457,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v } // Get the profile length. - unsigned int length = ringbuffer_distance (current, previous, 1, layout->rb_profile_begin, layout->rb_profile_end); + unsigned int length = ringbuffer_distance (current, previous, DC_RINGBUFFER_FULL, layout->rb_profile_begin, layout->rb_profile_end); // Check for a ringbuffer overflow. if (total + length > layout->rb_profile_end - layout->rb_profile_begin) { @@ -481,7 +481,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, eop); + rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, eop, DC_RBSTREAM_BACKWARD); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); return rc; @@ -510,7 +510,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v } // Get the profile length. - unsigned int length = ringbuffer_distance (current, previous, 1, layout->rb_profile_begin, layout->rb_profile_end); + unsigned int length = ringbuffer_distance (current, previous, DC_RINGBUFFER_FULL, layout->rb_profile_begin, layout->rb_profile_end); // Move to the begin of the current dive. offset -= length; diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c index dd941da7..b1ec8b4d 100644 --- a/src/cressi_leonardo.c +++ b/src/cressi_leonardo.c @@ -41,7 +41,7 @@ #define RB_PROFILE_BEGIN 0x1438 #define RB_PROFILE_END SZ_MEMORY -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_EMPTY, RB_PROFILE_BEGIN, RB_PROFILE_END) #define MAXRETRIES 4 #define PACKETSIZE 32 diff --git a/src/descriptor.c b/src/descriptor.c index f47cb479..4c465b77 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -275,6 +275,9 @@ static const dc_descriptor_t g_descriptors[] = { {"Aqualung", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Aqualung", "i200Cv2", DC_FAMILY_OCEANIC_ATOM2, 0x4749, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Oceanic", "Geo Air", DC_FAMILY_OCEANIC_ATOM2, 0x474B, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, + /* Pelagic I330R */ + {"Apeks", "DSX", DC_FAMILY_PELAGIC_I330R, 0x4741, DC_TRANSPORT_BLE, dc_filter_oceanic}, + {"Aqualung", "i330R", DC_FAMILY_PELAGIC_I330R, 0x4744, DC_TRANSPORT_BLE, dc_filter_oceanic}, /* Mares Nemo */ {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, @@ -365,6 +368,8 @@ static const dc_descriptor_t g_descriptors[] = { {"Shearwater", "Petrel 3", DC_FAMILY_SHEARWATER_PETREL, 10, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Perdix 2", DC_FAMILY_SHEARWATER_PETREL, 11, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Tern", DC_FAMILY_SHEARWATER_PETREL, 12, DC_TRANSPORT_BLE, dc_filter_shearwater}, + {"Shearwater", "Tern TX", DC_FAMILY_SHEARWATER_PETREL, 12, DC_TRANSPORT_BLE, dc_filter_shearwater}, + {"Shearwater", "Peregrine TX", DC_FAMILY_SHEARWATER_PETREL, 13, DC_TRANSPORT_BLE, dc_filter_shearwater}, /* Dive Rite NiTek Q */ {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL}, /* Citizen Hyper Aqualand */ @@ -685,6 +690,7 @@ dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, con "Perdix 2", "Teric", "Peregrine", + "Peregrine TX", "Tern" }; @@ -762,8 +768,10 @@ dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const 0x4654, // Oceanic Veo 4.0 0x4655, // Sherwood Wisdom 4 0x4656, // Oceanic Pro Plus 4 + 0x4741, // Apeks DSX 0x4742, // Sherwood Beacon 0x4743, // Aqualung i470TC + 0x4744, // Aqualung i330R 0x4749, // Aqualung i200C (newer model) 0x474B, // Oceanic Geo Air }; diff --git a/src/device.c b/src/device.c index f1a0ee7a..172f9411 100644 --- a/src/device.c +++ b/src/device.c @@ -38,6 +38,7 @@ #include "oceanic_atom2.h" #include "oceanic_veo250.h" #include "oceanic_vtpro.h" +#include "pelagic_i330r.h" #include "mares_darwin.h" #include "mares_iconhd.h" #include "mares_nemo.h" @@ -165,6 +166,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_OCEANIC_ATOM2: rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; + case DC_FAMILY_PELAGIC_I330R: + rc = pelagic_i330r_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); + break; case DC_FAMILY_MARES_NEMO: rc = mares_nemo_device_open (&device, context, iostream); break; diff --git a/src/divesoft_freedom_parser.c b/src/divesoft_freedom_parser.c index efad4081..5f83656e 100644 --- a/src/divesoft_freedom_parser.c +++ b/src/divesoft_freedom_parser.c @@ -252,6 +252,9 @@ typedef struct divesoft_freedom_parser_t { unsigned int seawater; unsigned int calibration[NSENSORS]; unsigned int calibrated; + unsigned int have_location; + int latitude; + int longitude; } divesoft_freedom_parser_t; static dc_status_t divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); @@ -394,6 +397,9 @@ divesoft_freedom_cache (divesoft_freedom_parser_t *parser) unsigned int gasmixid_previous = UNDEFINED; + unsigned int have_location = 0; + int latitude = 0; + int longitude = 0; // Parse the dive profile. unsigned int offset = headersize; @@ -544,6 +550,14 @@ divesoft_freedom_cache (divesoft_freedom_parser_t *parser) } tank[idx].endpressure = pressure; } + } else if (id == MEASURE_ID_GPS) { + if (!have_location) { + latitude = (signed int) array_uint32_le (data + offset + 4); + longitude = (signed int) array_uint32_le (data + offset + 8); + have_location = 1; + } else { + WARNING (abstract->context, "Multiple GPS locations present."); + } } } @@ -636,6 +650,9 @@ divesoft_freedom_cache (divesoft_freedom_parser_t *parser) parser->calibration[i] = calibration[i]; } parser->calibrated = calibrated; + parser->have_location = have_location; + parser->latitude = latitude; + parser->longitude = longitude; return DC_STATUS_SUCCESS; } @@ -690,6 +707,9 @@ divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context, const parser->calibration[i] = 0; } parser->calibrated = 0; + parser->have_location = 0; + parser->latitude = 0; + parser->longitude = 0; *out = (dc_parser_t *) parser; @@ -709,13 +729,21 @@ divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *date return status; unsigned int timestamp = array_uint32_le (data + 8); - dc_ticks_t ticks = (dc_ticks_t) timestamp + EPOCH; + + int timezone = 0; + if (parser->version == HEADER_SIGNATURE_V2) { + timezone = ((signed short) array_uint16_le (data + 40)) * 60; + } else { + timezone = 0; + } + + dc_ticks_t ticks = (dc_ticks_t) timestamp + EPOCH + timezone; if (!dc_datetime_gmtime (datetime, ticks)) return DC_STATUS_DATAFORMAT; if (parser->version == HEADER_SIGNATURE_V2) { - datetime->timezone = ((signed short) array_uint16_le (data + 40)) * 60; + datetime->timezone = timezone; } else { datetime->timezone = DC_TIMEZONE_NONE; } @@ -738,6 +766,7 @@ divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_tank_t *tank = (dc_tank_t *) value; dc_decomodel_t *decomodel = (dc_decomodel_t *) value; + dc_location_t *location = (dc_location_t *) value; if (value) { switch (type) { @@ -833,6 +862,13 @@ divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, decomodel->params.gf.high = parser->gf_hi; } break; + case DC_FIELD_LOCATION: + if (!parser->have_location) + return DC_STATUS_UNSUPPORTED; + location->latitude = parser->latitude / 1000000.0; + location->longitude = parser->longitude / 1000000.0; + location->altitude = 0.0; + break; default: return DC_STATUS_UNSUPPORTED; } diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index 3c80590d..1e7dd860 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -90,6 +90,10 @@ struct divesystem_idive_parser_t { unsigned int algorithm; unsigned int gf_low; unsigned int gf_high; + unsigned int have_location; + int latitude; + int longitude; + int altitude; }; static dc_status_t divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); @@ -149,7 +153,10 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, const parser->algorithm = INVALID; parser->gf_low = INVALID; parser->gf_high = INVALID; - + parser->have_location = 0; + parser->latitude = 0; + parser->longitude = 0; + parser->altitude = 0; *out = (dc_parser_t*) parser; @@ -276,6 +283,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, dc_tank_t *tank = (dc_tank_t *) value; dc_salinity_t *water = (dc_salinity_t *) value; dc_decomodel_t *decomodel = (dc_decomodel_t *) value; + dc_location_t *location = (dc_location_t *) value; if (value) { switch (type) { @@ -381,6 +389,13 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, } } break; + case DC_FIELD_LOCATION: + if (!parser->have_location) + return DC_STATUS_UNSUPPORTED; + location->latitude = parser->latitude / 10000000.0; + location->longitude = parser->longitude / 10000000.0; + location->altitude = parser->altitude / 1000.0; + break; default: return DC_STATUS_UNSUPPORTED; } @@ -438,6 +453,9 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba firmware = array_uint32_le(data + 0x2E); } + unsigned int have_location = 0; + int altitude = 0, longitude = 0, latitude = 0; + unsigned int offset = parser->headersize; while (offset + samplesize <= size) { dc_sample_value_t sample = {0}; @@ -447,6 +465,17 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba array_uint16_le (data + offset + 52) : REC_SAMPLE; if (type != REC_SAMPLE) { + if (type == REC_INFO) { + if (!have_location) { + altitude = (signed int) array_uint32_le (data + offset + 40); + longitude = (signed int) array_uint32_le (data + offset + 44); + latitude = (signed int) array_uint32_le (data + offset + 48); + have_location = 1; + } else { + WARNING (abstract->context, "Multiple GPS locations present."); + } + } + // Skip non-sample records. offset += samplesize; continue; @@ -649,6 +678,10 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba parser->algorithm = algorithm; parser->gf_low = gf_low; parser->gf_high = gf_high; + parser->have_location = have_location; + parser->latitude = latitude; + parser->longitude = longitude; + parser->altitude = altitude; parser->cached = 1; return DC_STATUS_SUCCESS; diff --git a/src/hw_frog.c b/src/hw_frog.c index f98523b5..2de4ebac 100644 --- a/src/hw_frog.c +++ b/src/hw_frog.c @@ -40,7 +40,7 @@ #define RB_PROFILE_BEGIN 0x000000 #define RB_PROFILE_END 0x200000 -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_EMPTY, RB_PROFILE_BEGIN, RB_PROFILE_END) #define READY 0x4D #define HEADER 0x61 diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index b755955e..3ce87438 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -67,12 +67,12 @@ #define RB_LOGBOOK_BEGIN (1 * PAGESIZE) #define RB_LOGBOOK_END (25 * PAGESIZE) #define RB_LOGBOOK_SIZE (RB_LOGBOOK_END - RB_LOGBOOK_BEGIN) -#define RB_LOGBOOK_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END) +#define RB_LOGBOOK_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END) #define RB_PROFILE_BEGIN (25 * PAGESIZE) #define RB_PROFILE_END (500 * PAGESIZE) #define RB_PROFILE_SIZE (RB_PROFILE_END - RB_PROFILE_BEGIN) -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_PROFILE_BEGIN, RB_PROFILE_END) +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, RB_PROFILE_BEGIN, RB_PROFILE_END) #define SZ_HEADER_XEN 80 #define SZ_HEADER_OTHER 96 @@ -469,7 +469,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb // Create the ringbuffer stream. dc_rbstream_t *rblogbook = NULL; - status = dc_rbstream_new (&rblogbook, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END, rb_logbook_end); + status = dc_rbstream_new (&rblogbook, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END, rb_logbook_end, DC_RBSTREAM_BACKWARD); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); goto error_free_logbook; @@ -600,7 +600,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb // Create the ringbuffer stream. dc_rbstream_t *rbprofile = NULL; - status = dc_rbstream_new (&rbprofile, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_PROFILE_BEGIN, RB_PROFILE_END, rb_profile_end); + status = dc_rbstream_new (&rbprofile, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_PROFILE_BEGIN, RB_PROFILE_END, rb_profile_end, DC_RBSTREAM_BACKWARD); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); goto error_free_profile; diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 03b00e51..92073ac3 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -80,7 +80,7 @@ #define VARIABLE 1 #define ACK 0xAA -#define EOF 0xEA +#define END 0xEA #define XOR 0xA5 #define CMD_VERSION 0xC2 @@ -230,7 +230,7 @@ mares_iconhd_packet_fixed (mares_iconhd_device_t *device, }; status = dc_iostream_write (device->iostream, command, sizeof(command), NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); + ERROR (abstract->context, "Failed to send the command header."); return status; } @@ -238,7 +238,7 @@ mares_iconhd_packet_fixed (mares_iconhd_device_t *device, if (size && transport == DC_TRANSPORT_BLE) { status = dc_iostream_write (device->iostream, data, size, NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to send the command."); + ERROR (abstract->context, "Failed to send the command data."); return status; } } @@ -247,13 +247,13 @@ mares_iconhd_packet_fixed (mares_iconhd_device_t *device, unsigned char header[1] = {0}; status = dc_iostream_read (device->iostream, header, sizeof (header), NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); + ERROR (abstract->context, "Failed to receive the packet header."); return status; } // Verify the header byte. if (header[0] != ACK) { - ERROR (abstract->context, "Unexpected answer byte."); + ERROR (abstract->context, "Unexpected packet header byte (%02x).", header[0]); return DC_STATUS_PROTOCOL; } @@ -269,7 +269,7 @@ mares_iconhd_packet_fixed (mares_iconhd_device_t *device, // Read the packet. status = dc_iostream_read (device->iostream, answer, asize, NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); + ERROR (abstract->context, "Failed to receive the packet data."); return status; } @@ -277,13 +277,13 @@ mares_iconhd_packet_fixed (mares_iconhd_device_t *device, unsigned char trailer[1] = {0}; status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL); if (status != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to receive the answer."); + ERROR (abstract->context, "Failed to receive the packet trailer."); return status; } // Verify the trailer byte. - if (trailer[0] != EOF) { - ERROR (abstract->context, "Unexpected answer byte."); + if (trailer[0] != END) { + ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", trailer[0]); return DC_STATUS_PROTOCOL; } @@ -358,7 +358,7 @@ mares_iconhd_packet_variable (mares_iconhd_device_t *device, } // Verify the trailer byte. - if (packet[length - 1] != EOF) { + if (packet[length - 1] != END) { ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", packet[length - 1]); return DC_STATUS_PROTOCOL; } @@ -876,7 +876,7 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - rc = dc_rbstream_new (&rbstream, abstract, 1, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop); + rc = dc_rbstream_new (&rbstream, abstract, 1, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop, DC_RBSTREAM_BACKWARD); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); return rc; diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c index d9f3a6d6..6c7da77c 100644 --- a/src/oceanic_atom2.c +++ b/src/oceanic_atom2.c @@ -86,6 +86,8 @@ static const oceanic_common_device_vtable_t oceanic_atom2_device_vtable = { NULL, /* timesync */ oceanic_atom2_device_close /* close */ }, + oceanic_common_device_devinfo, + oceanic_common_device_pointers, oceanic_common_device_logbook, oceanic_common_device_profile, }; @@ -98,10 +100,11 @@ static const oceanic_common_layout_t aeris_f10_layout = { 0x0100, /* rb_logbook_begin */ 0x0D80, /* rb_logbook_end */ 32, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0D80, /* rb_profile_begin */ 0x10000, /* rb_profile_end */ 0, /* pt_mode_global */ - 2, /* pt_mode_logbook */ + 3, /* pt_mode_logbook */ 0, /* pt_mode_serial */ }; @@ -113,10 +116,11 @@ static const oceanic_common_layout_t aeris_f11_layout = { 0x0100, /* rb_logbook_begin */ 0x0D80, /* rb_logbook_end */ 32, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0D80, /* rb_profile_begin */ 0x20000, /* rb_profile_end */ 0, /* pt_mode_global */ - 3, /* pt_mode_logbook */ + 2, /* pt_mode_logbook */ 0, /* pt_mode_serial */ }; @@ -128,6 +132,7 @@ static const oceanic_common_layout_t oceanic_default_layout = { 0x0240, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0x10000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -143,6 +148,7 @@ static const oceanic_common_layout_t oceanic_atom1_layout = { 0x0240, /* rb_logbook_begin */ 0x0440, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0440, /* rb_profile_begin */ 0x8000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -158,6 +164,7 @@ static const oceanic_common_layout_t oceanic_atom2a_layout = { 0x0240, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0xFE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -173,6 +180,7 @@ static const oceanic_common_layout_t oceanic_atom2b_layout = { 0x0240, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0xFE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -188,6 +196,7 @@ static const oceanic_common_layout_t oceanic_atom2c_layout = { 0x0240, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0xFFF0, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -203,6 +212,7 @@ static const oceanic_common_layout_t sherwood_wisdom_layout = { 0x03D0, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0xFE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -218,6 +228,7 @@ static const oceanic_common_layout_t oceanic_proplus3_layout = { 0x03E0, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0xFE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -233,6 +244,7 @@ static const oceanic_common_layout_t tusa_zenair_layout = { 0x0240, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0xFE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -248,6 +260,7 @@ static const oceanic_common_layout_t oceanic_oc1_layout = { 0x0240, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0x1FE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -263,6 +276,7 @@ static const oceanic_common_layout_t oceanic_oci_layout = { 0x10C0, /* rb_logbook_begin */ 0x1400, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x1400, /* rb_profile_begin */ 0x1FE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -278,6 +292,7 @@ static const oceanic_common_layout_t oceanic_atom3_layout = { 0x0400, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0x1FE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -293,6 +308,7 @@ static const oceanic_common_layout_t oceanic_vt4_layout = { 0x0420, /* rb_logbook_begin */ 0x0A40, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0A40, /* rb_profile_begin */ 0x1FE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -308,6 +324,7 @@ static const oceanic_common_layout_t hollis_tx1_layout = { 0x0780, /* rb_logbook_begin */ 0x1000, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x1000, /* rb_profile_begin */ 0x40000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -323,6 +340,7 @@ static const oceanic_common_layout_t oceanic_veo1_layout = { 0x0400, /* rb_logbook_begin */ 0x0400, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0400, /* rb_profile_begin */ 0x0400, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -338,6 +356,7 @@ static const oceanic_common_layout_t oceanic_reactpro_layout = { 0x0400, /* rb_logbook_begin */ 0x0600, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0600, /* rb_profile_begin */ 0xFE00, /* rb_profile_end */ 1, /* pt_mode_global */ @@ -353,6 +372,7 @@ static const oceanic_common_layout_t oceanic_proplusx_layout = { 0x1000, /* rb_logbook_begin */ 0x10000, /* rb_logbook_end */ 16, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x40000, /* rb_profile_begin */ 0x440000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -368,6 +388,7 @@ static const oceanic_common_layout_t aqualung_i770r_layout = { 0x2000, /* rb_logbook_begin */ 0x10000, /* rb_logbook_end */ 16, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x40000, /* rb_profile_begin */ 0x640000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -383,6 +404,7 @@ static const oceanic_common_layout_t aeris_a300cs_layout = { 0x0900, /* rb_logbook_begin */ 0x1000, /* rb_logbook_end */ 16, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x1000, /* rb_profile_begin */ 0x3FE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -398,6 +420,7 @@ static const oceanic_common_layout_t aqualung_i450t_layout = { 0x10C0, /* rb_logbook_begin */ 0x1400, /* rb_logbook_end */ 16, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x1400, /* rb_profile_begin */ 0x3FE00, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -415,13 +438,14 @@ static const oceanic_common_version_t versions[] = { {"MANTA R\0\0 512K", 0, MANTA, &oceanic_atom2c_layout}, {"2M ATOM r\0\0 512K", 0x3349, ATOM2, &oceanic_atom2a_layout}, {"2M ATOM r\0\0 512K", 0, ATOM2, &oceanic_atom2c_layout}, + {"OCE GEO R\0\0 512K", 0x3242, GEO, &oceanic_atom2a_layout}, + {"OCE GEO R\0\0 512K", 0, GEO, &oceanic_atom2c_layout}, {"INSIGHT2 \0\0 512K", 0, INSIGHT2, &oceanic_atom2a_layout}, {"OCEVEO30 \0\0 512K", 0, VEO30, &oceanic_atom2a_layout}, {"ATMOSAI R\0\0 512K", 0, ATMOSAI2, &oceanic_atom2a_layout}, {"PROPLUS2 \0\0 512K", 0, PROPLUS21, &oceanic_atom2a_layout}, {"OCEGEO20 \0\0 512K", 0, GEO20, &oceanic_atom2a_layout}, - {"OCE GEO R\0\0 512K", 0, GEO, &oceanic_atom2a_layout}, {"AQUAI200 \0\0 512K", 0, I200, &oceanic_atom2a_layout}, {"AQUA200C \0\0 512K", 0, I200C, &oceanic_atom2a_layout}, diff --git a/src/oceanic_atom2_parser.c b/src/oceanic_atom2_parser.c index fe50796d..a8304471 100644 --- a/src/oceanic_atom2_parser.c +++ b/src/oceanic_atom2_parser.c @@ -37,6 +37,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,6 +53,7 @@ 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; @@ -78,6 +85,11 @@ static const dc_parser_vtable_t oceanic_atom2_parser_vtable = { NULL /* destroy */ }; +static unsigned int +is_freedive (unsigned int divemode, unsigned int model) +{ + return divemode == FREEDIVE && model != DSX; +} 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) @@ -96,6 +108,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,6 +147,14 @@ 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; @@ -174,8 +195,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 +315,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. @@ -362,6 +395,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. @@ -369,7 +406,7 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser) unsigned int o2_offset = 0; unsigned int he_offset = 0; unsigned int o2_step = 1; - if (mode == FREEDIVE) { + if (is_freedive (mode, parser->model)) { ngasmixes = 0; } else if (parser->model == DATAMASK || parser->model == COMPUMASK) { ngasmixes = 1; @@ -419,6 +456,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 +480,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; } @@ -493,9 +545,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; @@ -514,23 +575,49 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns 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: @@ -560,7 +647,7 @@ oceanic_atom2_parser_vendor (oceanic_atom2_parser_t *parser, const unsigned char dc_sample_value_t sample = {0}; // Ignore empty samples. - if ((parser->mode != FREEDIVE && + if ((!is_freedive (parser->mode, parser->model) && array_isequal (data + offset, samplesize, 0x00)) || array_isequal (data + offset, samplesize, 0xFF)) { offset += samplesize; @@ -569,7 +656,7 @@ oceanic_atom2_parser_vendor (oceanic_atom2_parser_t *parser, const unsigned char // Get the sample type. unsigned int sampletype = data[offset + 0]; - if (parser->mode == FREEDIVE) + if (is_freedive (parser->mode, parser->model)) sampletype = 0; // Get the sample size. @@ -605,16 +692,20 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ unsigned int extratime = 0; 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 (!is_freedive (parser->mode, parser->model)) { + 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; @@ -622,7 +713,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ } unsigned int samplesize = PAGESIZE / 2; - if (parser->mode == FREEDIVE) { + if (is_freedive (parser->mode, parser->model)) { if (parser->model == F10A || parser->model == F10B || parser->model == F11A || parser->model == F11B || parser->model == MUNDIAL2 || parser->model == MUNDIAL3) { @@ -637,12 +728,14 @@ 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; - if (parser->mode == FREEDIVE) { + if (is_freedive (parser->mode, parser->model)) { have_temperature = 0; have_pressure = 0; } else if (parser->model == VEO30 || parser->model == OCS || @@ -653,7 +746,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,7 +758,7 @@ 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; @@ -687,7 +781,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ dc_sample_value_t sample = {0}; // Ignore empty samples. - if ((parser->mode != FREEDIVE && + if ((!is_freedive (parser->mode, parser->model) && array_isequal (data + offset, samplesize, 0x00)) || array_isequal (data + offset, samplesize, 0xFF)) { offset += samplesize; @@ -701,7 +795,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_ // Get the sample type. unsigned int sampletype = data[offset + 0]; - if (parser->mode == FREEDIVE) + if (is_freedive (parser->mode, parser->model)) sampletype = 0; // The sample size is usually fixed, but some sample types have a @@ -719,17 +813,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) { // 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 +916,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 +940,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,16 +969,22 @@ 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) unsigned int depth; - if (parser->mode == FREEDIVE) + if (is_freedive (parser->mode, parser->model)) depth = array_uint16_le (data + offset); else if (parser->model == GEO20 || parser->model == VEO20 || parser->model == VEO30 || parser->model == OC1A || @@ -891,11 +997,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 +1016,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 +1050,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 +1066,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 +1108,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 +1118,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 62f5f744..fc80bc11 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); - if (layout->pt_mode_logbook == 0) { - value = array_uint16_le (data + 5); - } 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); - } - - 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; -} - - -static unsigned int -get_profile_last (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int pagesize) -{ - unsigned int value; + // 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 + 6) >> 4; + 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 + 6); - } else if (layout->pt_mode_logbook == 3) { - value = array_uint16_le (data + 18); - } else { - return array_uint16_le(data + 18); + 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; + // 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; + } - if (npages > 0x4000) { - value &= 0x7FFF; - } else if (npages > 0x2000) { - value &= 0x3FFF; - } else if (npages > 0x1000) { - value &= 0x1FFF; - } else { - value &= 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); + + 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,53 +265,90 @@ 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; - dc_status_t rc = DC_STATUS_SUCCESS; assert (device != NULL); assert (device->layout != NULL); - assert (device->layout->rb_logbook_entry_size <= sizeof (device->fingerprint)); - assert (progress != 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; - // Erase the buffer. - 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) { + 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 rc; + return status; } - // Get the logbook pointers. + // 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); - if (rb_logbook_last < layout->rb_logbook_begin || - rb_logbook_last >= layout->rb_logbook_end) + 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; + + assert (device != NULL); + assert (device->layout != NULL); + assert (device->layout->rb_logbook_entry_size <= sizeof (device->fingerprint)); + assert (progress != NULL); + + const oceanic_common_layout_t *layout = device->layout; + + // Erase the buffer. + if (!dc_buffer_clear (logbook)) + return DC_STATUS_NOMEMORY; + + // 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 045dca41..43254a96 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/oceanic_veo250.c b/src/oceanic_veo250.c index 58a9ceb3..f39b1cdb 100644 --- a/src/oceanic_veo250.c +++ b/src/oceanic_veo250.c @@ -58,6 +58,8 @@ static const oceanic_common_device_vtable_t oceanic_veo250_device_vtable = { NULL, /* timesync */ oceanic_veo250_device_close /* close */ }, + oceanic_common_device_devinfo, + oceanic_common_device_pointers, oceanic_common_device_logbook, oceanic_common_device_profile, }; @@ -70,6 +72,7 @@ static const oceanic_common_layout_t oceanic_veo250_layout = { 0x0400, /* rb_logbook_begin */ 0x0600, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0600, /* rb_profile_begin */ 0x8000, /* rb_profile_end */ 1, /* pt_mode_global */ diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c index ef3d6d16..b63c0fb4 100644 --- a/src/oceanic_vtpro.c +++ b/src/oceanic_vtpro.c @@ -51,7 +51,8 @@ typedef struct oceanic_vtpro_device_t { oceanic_vtpro_protocol_t protocol; } oceanic_vtpro_device_t; -static dc_status_t oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook); +static dc_status_t oceanic_vtpro_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 dc_status_t oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end); static dc_status_t oceanic_vtpro_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static dc_status_t oceanic_vtpro_device_close (dc_device_t *abstract); @@ -67,6 +68,8 @@ static const oceanic_common_device_vtable_t oceanic_vtpro_device_vtable = { NULL, /* timesync */ oceanic_vtpro_device_close /* close */ }, + oceanic_common_device_devinfo, + oceanic_vtpro_device_pointers, oceanic_vtpro_device_logbook, oceanic_common_device_profile, }; @@ -79,6 +82,7 @@ static const oceanic_common_layout_t oceanic_vtpro_layout = { 0x0240, /* rb_logbook_begin */ 0x0440, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x0440, /* rb_profile_begin */ 0x8000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -94,6 +98,7 @@ static const oceanic_common_layout_t oceanic_wisdom_layout = { 0x03D0, /* rb_logbook_begin */ 0x05D0, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x05D0, /* rb_profile_begin */ 0x8000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -109,6 +114,7 @@ static const oceanic_common_layout_t aeris_500ai_layout = { 0x0200, /* rb_logbook_begin */ 0x0200, /* rb_logbook_end */ 8, /* rb_logbook_entry_size */ + 1, /* rb_logbook_direction */ 0x00200, /* rb_profile_begin */ 0x20000, /* rb_profile_end */ 0, /* pt_mode_global */ @@ -286,7 +292,49 @@ oceanic_vtpro_calibrate (oceanic_vtpro_device_t *device) } static dc_status_t -oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook) +oceanic_aeris500ai_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_vtpro_device_t *device = (oceanic_vtpro_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; + + // Read the pointer data. + unsigned char pointers[PAGESIZE] = {0}; + status = oceanic_vtpro_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 = pointers[0x02]; + unsigned int rb_logbook_last = pointers[0x03]; + unsigned int rb_profile_first = array_uint16_le (pointers + 4) * PAGESIZE; + unsigned int rb_profile_last = array_uint16_le (pointers + 6) * PAGESIZE; + + *rb_logbook_begin = rb_logbook_first; + *rb_logbook_end = rb_logbook_last; + *rb_profile_begin = rb_profile_first; + *rb_profile_end = rb_profile_last; + + return status; +} + +static dc_status_t +oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end) { dc_status_t rc = DC_STATUS_SUCCESS; oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract; @@ -297,36 +345,25 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p assert (device->base.layout->rb_logbook_begin == device->base.layout->rb_logbook_end); assert (progress != NULL); - const oceanic_common_layout_t *layout = device->base.layout; - // Erase the buffer. if (!dc_buffer_clear (logbook)) return DC_STATUS_NOMEMORY; - // Read the pointer data. - unsigned char pointers[PAGESIZE] = {0}; - rc = oceanic_vtpro_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 last = pointers[0x03]; + // Get the number of dives. + unsigned int ndives = end - begin + 1; // Update and emit a progress event. - progress->current += PAGESIZE; - progress->maximum += PAGESIZE + (last + 1) * PAGESIZE / 2; + progress->maximum += ndives * PAGESIZE / 2; device_event_emit (abstract, DC_EVENT_PROGRESS, progress); // Allocate memory for the logbook entries. - if (!dc_buffer_reserve (logbook, (last + 1) * PAGESIZE / 2)) + if (!dc_buffer_reserve (logbook, ndives * PAGESIZE / 2)) return DC_STATUS_NOMEMORY; // Send the logbook index command. unsigned char command[] = {0x52, - (last >> 8) & 0xFF, // high - (last ) & 0xFF, // low + begin & 0xFF, + end & 0xFF, 0x00}; rc = oceanic_vtpro_transfer (device, command, sizeof (command), NULL, 0); if (rc != DC_STATUS_SUCCESS) { @@ -335,7 +372,7 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p } // Read the logbook index. - for (unsigned int i = 0; i < last + 1; ++i) { + for (unsigned int i = 0; i < ndives; ++i) { // Receive the answer of the dive computer. unsigned char answer[PAGESIZE / 2 + 1] = {0}; rc = dc_iostream_read (device->iostream, answer, sizeof(answer), NULL); @@ -374,14 +411,26 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p } static dc_status_t -oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook) +oceanic_vtpro_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) +{ + oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract; + + if (device->base.model == AERIS500AI) { + return oceanic_aeris500ai_device_pointers (abstract, progress, rb_logbook_begin, rb_logbook_end, rb_profile_begin, rb_profile_end); + } else { + return oceanic_common_device_pointers (abstract, progress, rb_logbook_begin, rb_logbook_end, rb_profile_begin, rb_profile_end); + } +} + +static dc_status_t +oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end) { oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract; if (device->base.model == AERIS500AI) { - return oceanic_aeris500ai_device_logbook (abstract, progress, logbook); + return oceanic_aeris500ai_device_logbook (abstract, progress, logbook, begin, end); } else { - return oceanic_common_device_logbook (abstract, progress, logbook); + return oceanic_common_device_logbook (abstract, progress, logbook, begin, end); } } diff --git a/src/parser.c b/src/parser.c index d63c5c67..ba78399d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -127,6 +127,7 @@ 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 diff --git a/src/pelagic_i330r.c b/src/pelagic_i330r.c new file mode 100644 index 00000000..17aad3bf --- /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 00000000..23045cdf --- /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 */ diff --git a/src/rbstream.c b/src/rbstream.c index 07188e0f..f4da2d7a 100644 --- a/src/rbstream.c +++ b/src/rbstream.c @@ -28,11 +28,13 @@ struct dc_rbstream_t { dc_device_t *device; + dc_rbstream_direction_t direction; unsigned int pagesize; unsigned int packetsize; unsigned int begin; unsigned int end; unsigned int address; + unsigned int offset; unsigned int available; unsigned int skip; unsigned char cache[]; @@ -53,7 +55,7 @@ iceil (unsigned int x, unsigned int n) } dc_status_t -dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address) +dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address, dc_rbstream_direction_t direction) { dc_rbstream_t *rbstream = NULL; @@ -78,6 +80,18 @@ dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize return DC_STATUS_INVALIDARGS; } + // Ringbuffer boundaries should not be reversed. + if (begin > end) { + ERROR (device->context, "Ringbuffer boundaries reversed!"); + return DC_STATUS_INVALIDARGS; + } + + // Packet size should be smaller than the ringbuffer size. + if (packetsize > (end - begin)) { + ERROR (device->context, "Packet size larger than the ringbuffer size!"); + return DC_STATUS_INVALIDARGS; + } + // Address should be inside the ringbuffer. if (address < begin || address > end) { ERROR (device->context, "Address outside the ringbuffer!"); @@ -92,64 +106,64 @@ dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize } rbstream->device = device; + rbstream->direction = direction; rbstream->pagesize = pagesize; rbstream->packetsize = packetsize; rbstream->begin = begin; rbstream->end = end; - rbstream->address = iceil(address, pagesize); + if (direction == DC_RBSTREAM_FORWARD) { + rbstream->address = ifloor(address, pagesize); + rbstream->skip = address - rbstream->address; + } else { + rbstream->address = iceil(address, pagesize); + rbstream->skip = rbstream->address - address; + } + rbstream->offset = 0; rbstream->available = 0; - rbstream->skip = rbstream->address - address; *out = rbstream; return DC_STATUS_SUCCESS; } -dc_status_t -dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size) +static dc_status_t +dc_rbstream_read_backward (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size) { dc_status_t rc = DC_STATUS_SUCCESS; - if (rbstream == NULL) - return DC_STATUS_INVALIDARGS; - - unsigned int address = rbstream->address; - unsigned int available = rbstream->available; - unsigned int skip = rbstream->skip; - unsigned int nbytes = 0; unsigned int offset = size; while (nbytes < size) { - if (available == 0) { + if (rbstream->available == 0) { // Handle the ringbuffer wrap point. - if (address == rbstream->begin) - address = rbstream->end; + if (rbstream->address == rbstream->begin) + rbstream->address = rbstream->end; // Calculate the packet size. unsigned int len = rbstream->packetsize; - if (rbstream->begin + len > address) - len = address - rbstream->begin; - - // Move to the begin of the current packet. - address -= len; + if (rbstream->begin + len > rbstream->address) + len = rbstream->address - rbstream->begin; // Read the packet into the cache. - rc = dc_device_read (rbstream->device, address, rbstream->cache, rbstream->packetsize); + rc = dc_device_read (rbstream->device, rbstream->address - len, rbstream->cache, rbstream->packetsize); if (rc != DC_STATUS_SUCCESS) return rc; - available = len - skip; - skip = 0; + // Move to the end of the next packet. + rbstream->address -= len; + + rbstream->available = len - rbstream->skip; + rbstream->skip = 0; } - unsigned int length = available; + unsigned int length = rbstream->available; if (nbytes + length > size) length = size - nbytes; offset -= length; - available -= length; + rbstream->available -= length; - memcpy (data + offset, rbstream->cache + available, length); + memcpy (data + offset, rbstream->cache + rbstream->available, length); // Update and emit a progress event. if (progress) { @@ -160,13 +174,76 @@ dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsign nbytes += length; } - rbstream->address = address; - rbstream->available = available; - rbstream->skip = skip; + return rc; +} + +static dc_status_t +dc_rbstream_read_forward (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size) +{ + dc_status_t rc = DC_STATUS_SUCCESS; + + unsigned int nbytes = 0; + while (nbytes < size) { + if (rbstream->available == 0) { + // Handle the ringbuffer wrap point. + if (rbstream->address == rbstream->end) + rbstream->address = rbstream->begin; + + // Calculate the packet size. + unsigned int len = rbstream->packetsize; + if (rbstream->address + len > rbstream->end) + len = rbstream->end - rbstream->address; + + // Calculate the excess number of bytes. + unsigned int extra = rbstream->packetsize - len; + + // Read the packet into the cache. + rc = dc_device_read (rbstream->device, rbstream->address - extra, rbstream->cache, rbstream->packetsize); + if (rc != DC_STATUS_SUCCESS) + return rc; + + // Move to the begin of the next packet. + rbstream->address += len; + + rbstream->offset = extra + rbstream->skip; + rbstream->available = len - rbstream->skip; + rbstream->skip = 0; + } + + unsigned int length = rbstream->available; + if (nbytes + length > size) + length = size - nbytes; + + memcpy (data + nbytes, rbstream->cache + rbstream->offset, length); + + rbstream->offset += length; + rbstream->available -= length; + + // Update and emit a progress event. + if (progress) { + progress->current += length; + device_event_emit (rbstream->device, DC_EVENT_PROGRESS, progress); + } + + nbytes += length; + } return rc; } +dc_status_t +dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size) +{ + if (rbstream == NULL) + return DC_STATUS_INVALIDARGS; + + if (rbstream->direction == DC_RBSTREAM_FORWARD) { + return dc_rbstream_read_forward (rbstream, progress, data, size); + } else { + return dc_rbstream_read_backward (rbstream, progress, data, size); + } +} + dc_status_t dc_rbstream_free (dc_rbstream_t *rbstream) { diff --git a/src/rbstream.h b/src/rbstream.h index be1d8f6d..2ea07674 100644 --- a/src/rbstream.h +++ b/src/rbstream.h @@ -33,6 +33,14 @@ extern "C" { */ typedef struct dc_rbstream_t dc_rbstream_t; +/** + * The ringbuffer read direction. + */ +typedef enum dc_rbstream_direction_t { + DC_RBSTREAM_FORWARD, + DC_RBSTREAM_BACKWARD +} dc_rbstream_direction_t; + /** * Create a new ringbuffer stream. * @@ -43,11 +51,12 @@ typedef struct dc_rbstream_t dc_rbstream_t; * @param[in] begin The ringbuffer begin address. * @param[in] end The ringbuffer end address. * @param[in] address The stream start address. + * @param[in] direction The ringbuffer read direction. * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code * on failure. */ dc_status_t -dc_rbstream_new (dc_rbstream_t **rbstream, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address); +dc_rbstream_new (dc_rbstream_t **rbstream, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address, dc_rbstream_direction_t direction); /** * Read data from the ringbuffer stream. diff --git a/src/ringbuffer.c b/src/ringbuffer.c index 72318d06..bea496dd 100644 --- a/src/ringbuffer.c +++ b/src/ringbuffer.c @@ -25,39 +25,43 @@ static unsigned int -normalize (unsigned int a, unsigned int size) +modulo (unsigned int x, unsigned int n, unsigned int d) { - return a % size; -} - - -static unsigned int -distance (unsigned int a, unsigned int b, int mode, unsigned int size) -{ - if (a < b) { - return (b - a) % size; - } else if (a > b) { - return size - (a - b) % size; + unsigned int result = 0; + if (d > x) { +#if 0 + result = (n - (d - x) % n) % n; +#else + unsigned int m = (d - x) % n; + result = m ? n - m : m; +#endif } else { - return (mode == 0 ? 0 : size); + result = (x - d) % n; } -} - -static unsigned int -increment (unsigned int a, unsigned int delta, unsigned int size) -{ - return (a + delta) % size; + return result + d; } static unsigned int -decrement (unsigned int a, unsigned int delta, unsigned int size) +distance (unsigned int a, unsigned int b, unsigned int n, unsigned int mode) { - if (delta <= a) { - return (a - delta) % size; + unsigned int result = 0; + if (a > b) { +#if 0 + result = (n - (a - b) % n) % n; +#else + unsigned int m = (a - b) % n; + result = m ? n - m : m; +#endif + } else { + result = (b - a) % n; + } + + if (result == 0) { + return (mode == 0 ? 0 : n); } else { - return size - (delta - a) % size; + return result; } } @@ -65,38 +69,38 @@ decrement (unsigned int a, unsigned int delta, unsigned int size) unsigned int ringbuffer_normalize (unsigned int a, unsigned int begin, unsigned int end) { - assert (end >= begin); - assert (a >= begin); + assert (end > begin); - return normalize (a, end - begin); + unsigned int n = end - begin; + return modulo (a, n, begin); } unsigned int ringbuffer_distance (unsigned int a, unsigned int b, int mode, unsigned int begin, unsigned int end) { - assert (end >= begin); - assert (a >= begin); + assert (end > begin); - return distance (a, b, mode, end - begin); + unsigned int n = end - begin; + return distance (a, b, n, mode); } unsigned int ringbuffer_increment (unsigned int a, unsigned int delta, unsigned int begin, unsigned int end) { - assert (end >= begin); - assert (a >= begin); + assert (end > begin); - return increment (a - begin, delta, end - begin) + begin; + unsigned int n = end - begin; + return modulo (a + delta % n, n, begin); } unsigned int ringbuffer_decrement (unsigned int a, unsigned int delta, unsigned int begin, unsigned int end) { - assert (end >= begin); - assert (a >= begin); + assert (end > begin); - return decrement (a - begin, delta, end - begin) + begin; + unsigned int n = end - begin; + return modulo (a + n - delta % n, n, begin); } diff --git a/src/ringbuffer.h b/src/ringbuffer.h index 3915739b..e41cd5ac 100644 --- a/src/ringbuffer.h +++ b/src/ringbuffer.h @@ -26,6 +26,9 @@ extern "C" { #endif /* __cplusplus */ +#define DC_RINGBUFFER_EMPTY 0 +#define DC_RINGBUFFER_FULL 1 + unsigned int ringbuffer_normalize (unsigned int a, unsigned int begin, unsigned int end); diff --git a/src/seac_screen.c b/src/seac_screen.c index 9b3796f8..3015fd65 100644 --- a/src/seac_screen.c +++ b/src/seac_screen.c @@ -58,7 +58,7 @@ #define RB_PROFILE_BEGIN 0x010000 #define RB_PROFILE_END 0x200000 #define RB_PROFILE_SIZE (RB_PROFILE_END - RB_PROFILE_BEGIN) -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_PROFILE_BEGIN, RB_PROFILE_END) +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, RB_PROFILE_BEGIN, RB_PROFILE_END) #define RB_PROFILE_INCR(a,d) ringbuffer_increment (a, d, RB_PROFILE_BEGIN, RB_PROFILE_END) typedef struct seac_screen_device_t { @@ -546,7 +546,7 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - status = dc_rbstream_new (&rbstream, abstract, SZ_READ, SZ_READ, RB_PROFILE_BEGIN, RB_PROFILE_END, eop); + status = dc_rbstream_new (&rbstream, abstract, SZ_READ, SZ_READ, RB_PROFILE_BEGIN, RB_PROFILE_END, eop, DC_RBSTREAM_BACKWARD); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); goto error_free_profile; diff --git a/src/shearwater_common.c b/src/shearwater_common.c index 1b06cefd..83527b62 100644 --- a/src/shearwater_common.c +++ b/src/shearwater_common.c @@ -748,6 +748,9 @@ dc_status_t shearwater_common_get_model(shearwater_common_device_t *device, unsi case 0x1512: *model = PEREGRINE; break; + case 0x1712: + *model = PEREGRINE_TX; + break; case 0xC0E0: *model = TERN; break; diff --git a/src/shearwater_common.h b/src/shearwater_common.h index de9e2506..de0556bf 100644 --- a/src/shearwater_common.h +++ b/src/shearwater_common.h @@ -58,6 +58,7 @@ extern "C" { #define PETREL3 10 #define PERDIX2 11 #define TERN 12 +#define PEREGRINE_TX 13 #define NSTEPS 10000 #define STEP(i,n) ((NSTEPS * (i) + (n) / 2) / (n)) diff --git a/src/suunto_common.c b/src/suunto_common.c index 9988b86a..080b5301 100644 --- a/src/suunto_common.c +++ b/src/suunto_common.c @@ -27,7 +27,7 @@ #include "ringbuffer.h" #include "array.h" -#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_profile_begin, l->rb_profile_end) +#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, DC_RINGBUFFER_EMPTY, 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 diff --git a/src/suunto_common2.c b/src/suunto_common2.c index 063ea2b7..c26eb54e 100644 --- a/src/suunto_common2.c +++ b/src/suunto_common2.c @@ -297,7 +297,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%04x 0x%04x 0x%04x %u).", begin, last, end, count); remaining = layout->rb_profile_end - layout->rb_profile_begin; } else { - remaining = RB_PROFILE_DISTANCE (layout, begin, end, count != 0); + remaining = RB_PROFILE_DISTANCE (layout, begin, end, count ? DC_RINGBUFFER_FULL : DC_RINGBUFFER_EMPTY); } // Update and emit a progress event. @@ -307,7 +307,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, end); + rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, end, DC_RBSTREAM_BACKWARD); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); return rc; @@ -328,7 +328,7 @@ suunto_common2_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac unsigned int offset = remaining; while (offset) { // Calculate the size of the current dive. - unsigned int size = RB_PROFILE_DISTANCE (layout, current, previous, 1); + unsigned int size = RB_PROFILE_DISTANCE (layout, current, previous, DC_RINGBUFFER_FULL); if (size < 4 || size > offset) { ERROR (abstract->context, "Unexpected profile size (%u %u).", size, offset); diff --git a/src/suunto_solution.c b/src/suunto_solution.c index e72fe94e..e3e6b1cf 100644 --- a/src/suunto_solution.c +++ b/src/suunto_solution.c @@ -300,7 +300,7 @@ suunto_solution_extract_dives (dc_device_t *abstract, const unsigned char data[] // to find the start of the current dive. unsigned int peek = ringbuffer_increment (current, 2, RB_PROFILE_BEGIN, RB_PROFILE_END); if (data[peek] == 0x80) { - unsigned int len = ringbuffer_distance (previous, current, 0, RB_PROFILE_BEGIN, RB_PROFILE_END); + unsigned int len = ringbuffer_distance (previous, current, DC_RINGBUFFER_EMPTY, RB_PROFILE_BEGIN, RB_PROFILE_END); if (callback && !callback (buffer + idx, len, NULL, 0, userdata)) return DC_STATUS_SUCCESS; diff --git a/src/uwatec_aladin.c b/src/uwatec_aladin.c index 1cfec542..ee154331 100644 --- a/src/uwatec_aladin.c +++ b/src/uwatec_aladin.c @@ -36,7 +36,7 @@ #define RB_PROFILE_BEGIN 0x000 #define RB_PROFILE_END 0x600 #define RB_PROFILE_NEXT(a) ringbuffer_increment (a, 1, RB_PROFILE_BEGIN, RB_PROFILE_END) -#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) +#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_EMPTY, RB_PROFILE_BEGIN, RB_PROFILE_END) #define HEADER 4 diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c index e0d34c1b..981efc5e 100644 --- a/src/uwatec_smart.c +++ b/src/uwatec_smart.c @@ -31,7 +31,8 @@ #define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_smart_device_vtable) -#define DATASIZE 254 +#define DATASIZE_RX 255 +#define DATASIZE_TX 254 #define MAX_PACKETSIZE 256 #define PACKETSIZE_USBHID_RX 64 #define PACKETSIZE_USBHID_TX 32 @@ -90,13 +91,13 @@ uwatec_smart_irda_send (uwatec_smart_device_t *device, unsigned char cmd, const dc_status_t rc = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; - if (size > DATASIZE) { + if (size > DATASIZE_TX) { ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size); return DC_STATUS_PROTOCOL; } // Build the packet. - unsigned char packet[1 + DATASIZE] = { + unsigned char packet[1 + DATASIZE_TX] = { cmd}; if (size) { memcpy (packet + 1, data, size); @@ -157,13 +158,13 @@ uwatec_smart_serial_send (uwatec_smart_device_t *device, unsigned char cmd, cons dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; - if (size > DATASIZE) { + if (size > DATASIZE_TX) { ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size); return DC_STATUS_PROTOCOL; } // Build the packet. - unsigned char packet[12 + DATASIZE + 1] = { + unsigned char packet[12 + DATASIZE_TX + 1] = { 0xFF, 0xFF, 0xFF, 0xA6, 0x59, 0xBD, 0xC2, size + 1, @@ -277,12 +278,12 @@ uwatec_smart_usbhid_send (uwatec_smart_device_t *device, unsigned char cmd, cons dc_status_t rc = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; dc_transport_t transport = dc_iostream_get_transport(device->iostream); - unsigned char buf[DATASIZE + 3]; + unsigned char buf[DATASIZE_TX + 3]; size_t packetsize = transport == DC_TRANSPORT_USBHID ? PACKETSIZE_USBHID_TX + 1 : sizeof(buf); - if (size > DATASIZE || size + 3 > packetsize) { + if (size > DATASIZE_TX || size + 3 > packetsize) { ERROR (abstract->context, "Command too large (" DC_PRINTF_SIZE ").", size); return DC_STATUS_INVALIDARGS; } @@ -316,7 +317,7 @@ uwatec_smart_usbhid_receive (uwatec_smart_device_t *device, dc_event_progress_t dc_status_t rc = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; dc_transport_t transport = dc_iostream_get_transport(device->iostream); - unsigned char buf[DATASIZE + 1]; + unsigned char buf[DATASIZE_RX + 1]; size_t packetsize = transport == DC_TRANSPORT_USBHID ? PACKETSIZE_USBHID_RX : sizeof(buf); diff --git a/src/zeagle_n2ition3.c b/src/zeagle_n2ition3.c index 420ac0c3..0d43122e 100644 --- a/src/zeagle_n2ition3.c +++ b/src/zeagle_n2ition3.c @@ -277,7 +277,7 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba } // Get the number of logbook items. - unsigned int count = ringbuffer_distance (first, last, 0, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END) + 1; + unsigned int count = ringbuffer_distance (first, last, DC_RINGBUFFER_EMPTY, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END) + 1; // Get the profile pointer. unsigned int eop = array_uint16_le (config + 0x7E); @@ -302,7 +302,7 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba } // Get the profile length. - unsigned int length = ringbuffer_distance (current, previous, 1, RB_PROFILE_BEGIN, RB_PROFILE_END); + unsigned int length = ringbuffer_distance (current, previous, DC_RINGBUFFER_FULL, RB_PROFILE_BEGIN, RB_PROFILE_END); // Check for a ringbuffer overflow. if (total + length > RB_PROFILE_END - RB_PROFILE_BEGIN) { @@ -326,7 +326,7 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba // Create the ringbuffer stream. dc_rbstream_t *rbstream = NULL; - rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, RB_PROFILE_BEGIN, RB_PROFILE_END, eop); + rc = dc_rbstream_new (&rbstream, abstract, 1, SZ_PACKET, RB_PROFILE_BEGIN, RB_PROFILE_END, eop, DC_RBSTREAM_BACKWARD); if (rc != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to create the ringbuffer stream."); return rc; @@ -344,7 +344,7 @@ zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba unsigned int current = array_uint16_le (config + 2 * idx); // Get the profile length. - unsigned int length = ringbuffer_distance (current, previous, 1, RB_PROFILE_BEGIN, RB_PROFILE_END); + unsigned int length = ringbuffer_distance (current, previous, DC_RINGBUFFER_FULL, RB_PROFILE_BEGIN, RB_PROFILE_END); // Move to the begin of the current dive. offset -= length;