From 7e8e9130ccd878e68d2b482e21f073da8fbfc0e8 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 11 Jan 2024 21:14:20 +0100 Subject: [PATCH 1/6] Increase the BLE packet size Devices supporting BLE 4.2 (or higher) can use data packets with a payload size of up to 244 bytes. None of the current Mares models support this at the moment, but increasing the size on the receiving side already prepares the code for future models and remains fully backwards compatible. Signed-off-by: rmultan --- src/mares_iconhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index b19f5bff..e2e1248b 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -414,7 +414,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ // Create the packet stream. if (transport == DC_TRANSPORT_BLE) { - status = dc_packet_open (&device->iostream, context, iostream, 20, 20); + status = dc_packet_open (&device->iostream, context, iostream, 244, 20); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to create the packet stream."); goto error_free; From e0662af63d0b3d20ef4a7487fb9ac2c1660bf36e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 11 Jan 2024 21:20:22 +0100 Subject: [PATCH 2/6] Use macros to detect the device type The macros make the code a bit more compact, and adding support for new models becomes easier too. Signed-off-by: rmultan --- src/mares_iconhd.c | 13 +++++++++-- src/mares_iconhd_parser.c | 47 +++++++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index e2e1248b..e6c8f55d 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -49,6 +49,15 @@ #define QUAD 0x29 #define HORIZON 0x2C +#define ISSMART(model) ( \ + (model) == SMART || \ + (model) == SMARTAPNEA || \ + (model) == SMARTAIR) + +#define ISGENIUS(model) ( \ + (model) == GENIUS || \ + (model) == HORIZON) + #define MAXRETRIES 4 #define ACK 0xAA @@ -735,7 +744,7 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb // Get the number of samples in the profile data. unsigned int type = 0, nsamples = 0; - if (model == SMART || model == SMARTAPNEA || model == SMARTAIR) { + if (ISSMART(model)) { type = array_uint16_le (buffer + offset - header + 2); nsamples = array_uint16_le (buffer + offset - header + 0); } else { @@ -950,7 +959,7 @@ mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, devinfo.serial = array_uint32_le (serial); device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - if (device->model == GENIUS || device->model == HORIZON) { + if (ISGENIUS(device->model)) { return mares_iconhd_device_foreach_object (abstract, callback, userdata); } else { return mares_iconhd_device_foreach_raw (abstract, callback, userdata); diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index c845e65f..c5b4dd7f 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -48,6 +48,15 @@ #define SMARTAIR 0x24 #define HORIZON 0x2C +#define ISSMART(model) ( \ + (model) == SMART || \ + (model) == SMARTAPNEA || \ + (model) == SMARTAIR) + +#define ISGENIUS(model) ( \ + (model) == GENIUS || \ + (model) == HORIZON) + #define NGASMIXES_ICONHD 3 #define NGASMIXES_GENIUS 5 #define NGASMIXES NGASMIXES_GENIUS @@ -335,7 +344,7 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser) // Get the number of samples in the profile data. unsigned int type = 0, nsamples = 0; - if (parser->model == SMART || parser->model == SMARTAPNEA || parser->model == SMARTAIR) { + if (ISSMART (parser->model)) { type = array_uint16_le (data + length - header + 2); nsamples = array_uint16_le (data + length - header + 0); } else { @@ -390,7 +399,7 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser) } const unsigned char *p = data + length - headersize; - if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) { + if (!ISSMART(parser->model)) { p += 4; } @@ -649,7 +658,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser) return DC_STATUS_SUCCESS; } - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { return mares_genius_cache (parser); } else { return mares_iconhd_cache (parser); @@ -675,7 +684,7 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsi parser->model = model; parser->cached = 0; parser->logformat = 0; - parser->mode = (model == GENIUS || model == HORIZON) ? GENIUS_AIR : ICONHD_AIR; + parser->mode = ISGENIUS(model) ? GENIUS_AIR : ICONHD_AIR; parser->nsamples = 0; parser->samplesize = 0; parser->headersize = 0; @@ -715,9 +724,9 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime // Pointer to the header data. const unsigned char *p = abstract->data; - if (parser->model != GENIUS && parser->model != HORIZON) { + if (!ISGENIUS(parser->model)) { p += abstract->size - parser->headersize; - if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) { + if (!ISSMART(parser->model)) { p += 4; } } @@ -726,7 +735,7 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime p += parser->layout->datetime; if (datetime) { - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { unsigned int timestamp = array_uint32_le (p); datetime->hour = (timestamp ) & 0x1F; datetime->minute = (timestamp >> 5) & 0x3F; @@ -762,9 +771,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi // Pointer to the header data. const unsigned char *p = abstract->data; - if (parser->model != GENIUS && parser->model != HORIZON) { + if (!ISGENIUS(parser->model)) { p += abstract->size - parser->headersize; - if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) { + if (!ISSMART(parser->model)) { p += 4; } } @@ -775,7 +784,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi extra = 8; } - unsigned int metric = (parser->model == GENIUS || parser->model == HORIZON) ? + unsigned int metric = ISGENIUS(parser->model) ? p[0x34 + extra] : parser->settings & 0x0100; dc_gasmix_t *gasmix = (dc_gasmix_t *) value; @@ -834,7 +843,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi *((double *) value) = array_uint16_le (p + parser->layout->atmospheric) / (1000.0 * parser->layout->atmospheric_divisor); break; case DC_FIELD_SALINITY: - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { unsigned int salinity = (parser->settings >> 5) & 0x03; switch (salinity) { case WATER_FRESH: @@ -876,7 +885,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi *((double *) value) = (signed short) array_uint16_le (p + parser->layout->temperature_max) / 10.0; break; case DC_FIELD_DIVEMODE: - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { switch (parser->mode) { case GENIUS_AIR: case GENIUS_NITROX_SINGLE: @@ -951,7 +960,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int offset = 4; unsigned int marker = 0; - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { // Skip the dive header. data += parser->headersize; @@ -1021,7 +1030,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t offset += 2; } - } else if (parser->model != GENIUS && parser->model != HORIZON && parser->mode == ICONHD_FREEDIVE) { + } else if (!ISGENIUS(parser->model) && parser->mode == ICONHD_FREEDIVE) { unsigned int maxdepth = array_uint16_le (data + offset + 0); unsigned int divetime = array_uint16_le (data + offset + 2); unsigned int surftime = array_uint16_le (data + offset + 4); @@ -1051,7 +1060,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int gasmix = 0, alarms = 0; unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0; unsigned int bookmark = 0; - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { if (parser->logformat == 1) { if (!mares_genius_isvalid (data + offset, SDPT_SIZE, SDPT_TYPE)) { ERROR (abstract->context, "Invalid SDPT record."); @@ -1132,7 +1141,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); } - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { // Deco stop / NDL. if (decostop) { sample.deco.type = DC_DECO_DECOSTOP; @@ -1179,7 +1188,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t // Some extra data. if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) { - if ((parser->model == GENIUS || parser->model == HORIZON) && + if (ISGENIUS(parser->model) && !mares_genius_isvalid (data + offset, AIRS_SIZE, AIRS_TYPE)) { ERROR (abstract->context, "Invalid AIRS record."); return DC_STATUS_DATAFORMAT; @@ -1195,12 +1204,12 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t WARNING (abstract->context, "Invalid tank with non-zero pressure."); } - offset += (parser->model == GENIUS || parser->model == HORIZON) ? AIRS_SIZE : 8; + offset += ISGENIUS(parser->model) ? AIRS_SIZE : 8; } } } - if (parser->model == GENIUS || parser->model == HORIZON) { + if (ISGENIUS(parser->model)) { // Skip the DEND record. if (!mares_genius_isvalid (data + offset, DEND_SIZE, DEND_TYPE)) { ERROR (abstract->context, "Invalid DEND record."); From 04f7274bda1acd8e38f5c9e1517658e7b2e70d8d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 16 Jan 2024 19:23:08 +0100 Subject: [PATCH 3/6] Use a new command to read the serial number The Mares Genius (and compatible models) uses a new command to download different types of objects, instead of manually reading and parsing the flash memory. This command also supports reading device properties like the serial number. This change is necessary because newer models like the Sirius no longer support reading directly from the flash memory. Signed-off-by: rmultan --- src/mares_iconhd.c | 60 +++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index e6c8f55d..2e8aaad3 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -71,6 +71,8 @@ #define CMD_OBJ_EVEN 0xAC #define CMD_OBJ_ODD 0xFE +#define OBJ_DEVICE 0x2000 +#define OBJ_DEVICE_SERIAL 0x04 #define OBJ_LOGBOOK 0x2008 #define OBJ_LOGBOOK_COUNT 0x01 #define OBJ_DIVE 0x3000 @@ -668,6 +670,21 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb const mares_iconhd_layout_t *layout = device->layout; + // Read the serial number. + unsigned char serial[4] = {0}; + rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial)); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the memory."); + return rc; + } + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = array_uint32_le (serial); + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + // Enable progress notifications. dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; progress.maximum = layout->rb_profile_end - layout->rb_profile_begin; @@ -873,6 +890,33 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca return DC_STATUS_NOMEMORY; } + // Read the serial number. + rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_DEVICE, OBJ_DEVICE_SERIAL); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the serial number."); + dc_buffer_free (buffer); + return rc; + } + + if (dc_buffer_get_size (buffer) < 16) { + ERROR (abstract->context, "Unexpected number of bytes received (" DC_PRINTF_SIZE ").", + dc_buffer_get_size (buffer)); + dc_buffer_free (buffer); + return DC_STATUS_PROTOCOL; + } + + unsigned int serial = array_convert_str2num (dc_buffer_get_data (buffer) + 10, 6); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = device->model; + devinfo.firmware = 0; + devinfo.serial = serial; + device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); + + // Erase the buffer. + dc_buffer_clear (buffer); + // Read the number of dives. rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_LOGBOOK, OBJ_LOGBOOK_COUNT); if (rc != DC_STATUS_SUCCESS) { @@ -935,7 +979,6 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) { - dc_status_t rc = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; // Emit a vendor event. @@ -944,21 +987,6 @@ mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, vendor.size = sizeof (device->version); device_event_emit (abstract, DC_EVENT_VENDOR, &vendor); - // Read the serial number. - unsigned char serial[4] = {0}; - rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial)); - if (rc != DC_STATUS_SUCCESS) { - ERROR (abstract->context, "Failed to read the memory."); - return rc; - } - - // Emit a device info event. - dc_event_devinfo_t devinfo; - devinfo.model = device->model; - devinfo.firmware = 0; - devinfo.serial = array_uint32_le (serial); - device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo); - if (ISGENIUS(device->model)) { return mares_iconhd_device_foreach_object (abstract, callback, userdata); } else { From d3ab3d314981258ebc379f128296a494ed6c05c8 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 19 Apr 2024 19:15:34 +0200 Subject: [PATCH 4/6] Refactor the Mares Genius and Horizon parser The current implementation assumes a fixed order for the different record types to share some code with the older models. See commits [1] and [2] for more details. The main disadvantages of this approach are the extra complexity and the limited flexibility to adapt to future changes to the data format. To make the parsing code more future proof, split the code into separate functions for the Genius/Horizon and the older models. [1] Commit 8b06f2c31d437d6e067c21e7263b5ccc33539537 (Horizon) [2] Commit feec939a2924095f07e030aa98d839b40d4cb6cc (Genius) Signed-off-by: rmultan --- src/mares_iconhd_parser.c | 434 +++++++++++++++++++++----------------- 1 file changed, 243 insertions(+), 191 deletions(-) diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index c5b4dd7f..a9d87dc4 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -292,28 +292,6 @@ static const dc_parser_vtable_t mares_iconhd_parser_vtable = { NULL /* destroy */ }; -static unsigned int -mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type) -{ - if (size < 10) { - return 0; - } - - unsigned int head = array_uint32_be(data); - unsigned int tail = array_uint32_be(data + size - 4); - if (head != type || tail != type) { - return 0; - } - - unsigned short crc = array_uint16_le(data + size - 6); - unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000); - if (crc != ccrc) { - return 0; - } - - return 1; -} - static dc_status_t mares_iconhd_cache (mares_iconhd_parser_t *parser) { @@ -557,30 +535,22 @@ mares_genius_cache (mares_iconhd_parser_t *parser) // Get the dive mode. unsigned int mode = settings & 0xF; - // Get the sample size. - unsigned int samplesize = logformat == 1 ? SDPT_SIZE: DPRS_SIZE; - - // Calculate the total number of bytes for this dive. - unsigned int nbytes = headersize + 4 + DSTR_SIZE + TISS_SIZE + nsamples * samplesize + (nsamples / 4) * AIRS_SIZE + DEND_SIZE; - if (nbytes > size) { - ERROR (abstract->context, "Buffer overflow detected!"); - return DC_STATUS_DATAFORMAT; - } - - // Get the profile type and version. - unsigned int profile_type = array_uint16_le (data + headersize); - unsigned int profile_minor = data[headersize + 2]; - unsigned int profile_major = data[headersize + 3]; - // Get the surface timeout setting (in minutes). // For older firmware versions the value is hardcoded to 3 minutes, but // starting with the newer v01.02.00 firmware the value is configurable and // stored in the settings. To detect whether the setting is available, we // need to check the profile version instead of the header version. unsigned int surftime = 3; - if (profile_type == 0 && - OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) { - surftime = (settings >> 13) & 0x3F; + if (headersize + 4 <= size) { + // Get the profile type and version. + unsigned int profile_type = array_uint16_le (data + headersize); + unsigned int profile_minor = data[headersize + 2]; + unsigned int profile_major = data[headersize + 3]; + + if (profile_type == 0 && + OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) { + surftime = (settings >> 13) & 0x3F; + } } // Gas mixes and tanks. @@ -631,7 +601,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser) parser->logformat = logformat; parser->mode = mode; parser->nsamples = nsamples; - parser->samplesize = samplesize; + parser->samplesize = 0; parser->headersize = headersize; parser->settings = settings; parser->surftime = surftime * 60; @@ -944,15 +914,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi static dc_status_t -mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +mares_iconhd_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) { mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract; - - // Cache the parser data. - dc_status_t rc = mares_iconhd_parser_cache (parser); - if (rc != DC_STATUS_SUCCESS) - return rc; - const unsigned char *data = abstract->data; // Previous gas mix - initialize with impossible value @@ -960,40 +924,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t unsigned int offset = 4; unsigned int marker = 0; - if (ISGENIUS(parser->model)) { - // Skip the dive header. - data += parser->headersize; - - // Check the profile type and version. - unsigned int type = array_uint16_le (data); - unsigned int minor = data[2]; - unsigned int major = data[3]; - if (type > 1 || - (type == 0 && OBJVERSION(major,minor) > OBJVERSION(1,0)) || - (type == 1 && OBJVERSION(major,minor) > OBJVERSION(0,2))) { - ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).", - type, major, minor); - return DC_STATUS_DATAFORMAT; - } - - // Skip the DSTR record. - if (!mares_genius_isvalid (data + offset, DSTR_SIZE, DSTR_TYPE)) { - ERROR (abstract->context, "Invalid DSTR record."); - return DC_STATUS_DATAFORMAT; - } - offset += DSTR_SIZE; - - // Skip the TISS record. - if (!mares_genius_isvalid (data + offset, TISS_SIZE, TISS_TYPE)) { - ERROR (abstract->context, "Invalid TISS record."); - return DC_STATUS_DATAFORMAT; - } - offset += TISS_SIZE; - - // Size of the record type marker. - marker = 4; - } - unsigned int time = 0; unsigned int nsamples = 0; while (nsamples < parser->nsamples) { @@ -1030,7 +960,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t offset += 2; } - } else if (!ISGENIUS(parser->model) && parser->mode == ICONHD_FREEDIVE) { + } else if (parser->mode == ICONHD_FREEDIVE) { unsigned int maxdepth = array_uint16_le (data + offset + 0); unsigned int divetime = array_uint16_le (data + offset + 2); unsigned int surftime = array_uint16_le (data + offset + 4); @@ -1056,54 +986,179 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t offset += parser->samplesize; nsamples++; } else { + unsigned int depth = array_uint16_le (data + offset + 0); + unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF; + unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4; + + // Time (seconds). + time += parser->interval; + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, &sample, userdata); + + // Depth (1/10 m). + sample.depth = depth / 10.0; + if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata); + + // Temperature (1/10 °C). + sample.temperature = temperature / 10.0; + if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata); + + // Current gas mix + if (parser->ngasmixes > 0) { + if (gasmix >= parser->ngasmixes) { + ERROR (abstract->context, "Invalid gas mix index."); + return DC_STATUS_DATAFORMAT; + } + if (gasmix != gasmix_previous) { + sample.gasmix = gasmix; + if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata); + gasmix_previous = gasmix; + } + } + + offset += parser->samplesize; + nsamples++; + + // Some extra data. + if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) { + // Pressure (1/100 bar). + unsigned int pressure = array_uint16_le(data + offset + marker + 0); + if (gasmix < parser->ntanks) { + sample.pressure.tank = gasmix; + sample.pressure.value = pressure / 100.0; + if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); + } else if (pressure != 0) { + WARNING (abstract->context, "Invalid tank with non-zero pressure."); + } + + offset += 8; + } + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +mares_genius_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract; + const unsigned char *data = abstract->data; + const unsigned int size = abstract->size; + + // Previous gas mix - initialize with impossible value + unsigned int gasmix_previous = 0xFFFFFFFF; + unsigned int tank = 0xFFFFFFFF; + + // Skip the dive header. + unsigned int offset = parser->headersize; + + if (offset + 4 > size) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + // Check the profile type and version. + unsigned int profile_type = array_uint16_le (data + offset); + unsigned int profile_minor = data[offset + 2]; + unsigned int profile_major = data[offset + 3]; + if (profile_type > 1 || + (profile_type == 0 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(1,0)) || + (profile_type == 1 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(0,2))) { + ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).", + profile_type, profile_major, profile_minor); + return DC_STATUS_DATAFORMAT; + } + offset += 4; + + unsigned int time = 0; + unsigned int marker = 4; + while (offset < size) { + dc_sample_value_t sample = {0}; + + if (offset + 10 > size) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + // Get the record type and length. + unsigned int type = array_uint32_be(data + offset); + unsigned int length = 0; + switch (type) { + case DSTR_TYPE: + length = DSTR_SIZE; + break; + case TISS_TYPE: + length = TISS_SIZE; + break; + case DPRS_TYPE: + length = DPRS_SIZE; + break; + case SDPT_TYPE: + length = SDPT_SIZE; + break; + case AIRS_TYPE: + length = AIRS_SIZE; + break; + case DEND_TYPE: + length = DEND_SIZE; + break; + default: + ERROR (abstract->context, "Unknown record type (%08x).", type); + return DC_STATUS_DATAFORMAT; + } + + if (offset + length > size) { + ERROR (abstract->context, "Buffer overflow detected!"); + return DC_STATUS_DATAFORMAT; + } + + unsigned int etype = array_uint32_be(data + offset + length - 4); + if (etype != type) { + ERROR (abstract->context, "Invalid record end type (%08x).", etype); + return DC_STATUS_DATAFORMAT; + } + + unsigned short crc = array_uint16_le(data + offset + length - 6); + unsigned short ccrc = checksum_crc16_ccitt(data + offset + 4, length - 10, 0x0000, 0x0000); + if (crc != ccrc) { + ERROR (abstract->context, "Invalid record checksum (%04x %04x).", crc, ccrc); + return DC_STATUS_DATAFORMAT; + } + + if (type == DPRS_TYPE || type == SDPT_TYPE) { unsigned int depth = 0, temperature = 0; unsigned int gasmix = 0, alarms = 0; unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0; unsigned int bookmark = 0; - if (ISGENIUS(parser->model)) { - if (parser->logformat == 1) { - if (!mares_genius_isvalid (data + offset, SDPT_SIZE, SDPT_TYPE)) { - ERROR (abstract->context, "Invalid SDPT record."); - return DC_STATUS_DATAFORMAT; - } - - unsigned int misc = 0, deco = 0; - depth = array_uint16_le (data + offset + marker + 2); - temperature = array_uint16_le (data + offset + marker + 6); - alarms = array_uint32_le (data + offset + marker + 0x14); - misc = array_uint32_le (data + offset + marker + 0x18); - deco = array_uint32_le (data + offset + marker + 0x1C); - bookmark = (misc >> 2) & 0x0F; - gasmix = (misc >> 6) & 0x0F; - decostop = (misc >> 10) & 0x01; - if (decostop) { - decodepth = (deco >> 3) & 0x7F; - decotime = (deco >> 10) & 0xFF; - tts = (deco >> 18) & 0x3FFF; - } else { - decotime = deco & 0xFF; - } + if (type == SDPT_TYPE) { + unsigned int misc = 0, deco = 0; + depth = array_uint16_le (data + offset + marker + 2); + temperature = array_uint16_le (data + offset + marker + 6); + alarms = array_uint32_le (data + offset + marker + 0x14); + misc = array_uint32_le (data + offset + marker + 0x18); + deco = array_uint32_le (data + offset + marker + 0x1C); + bookmark = (misc >> 2) & 0x0F; + gasmix = (misc >> 6) & 0x0F; + decostop = (misc >> 10) & 0x01; + if (decostop) { + decodepth = (deco >> 3) & 0x7F; + decotime = (deco >> 10) & 0xFF; + tts = (deco >> 18) & 0x3FFF; } else { - if (!mares_genius_isvalid (data + offset, DPRS_SIZE, DPRS_TYPE)) { - ERROR (abstract->context, "Invalid DPRS record."); - return DC_STATUS_DATAFORMAT; - } - - unsigned int misc = 0; - depth = array_uint16_le (data + offset + marker + 0); - temperature = array_uint16_le (data + offset + marker + 4); - decotime = array_uint16_le (data + offset + marker + 0x0A); - alarms = array_uint32_le (data + offset + marker + 0x0C); - misc = array_uint32_le (data + offset + marker + 0x14); - bookmark = (misc >> 2) & 0x0F; - gasmix = (misc >> 6) & 0x0F; - decostop = (misc >> 18) & 0x01; - decodepth = (misc >> 19) & 0x7F; + decotime = deco & 0xFF; } } else { - depth = array_uint16_le (data + offset + 0); - temperature = array_uint16_le (data + offset + 2) & 0x0FFF; - gasmix = (data[offset + 3] & 0xF0) >> 4; + unsigned int misc = 0; + depth = array_uint16_le (data + offset + marker + 0); + temperature = array_uint16_le (data + offset + marker + 4); + decotime = array_uint16_le (data + offset + marker + 0x0A); + alarms = array_uint32_le (data + offset + marker + 0x0C); + misc = array_uint32_le (data + offset + marker + 0x14); + bookmark = (misc >> 2) & 0x0F; + gasmix = (misc >> 6) & 0x0F; + decostop = (misc >> 18) & 0x01; + decodepth = (misc >> 19) & 0x7F; } // Time (seconds). @@ -1132,6 +1187,9 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t } } + // Current tank + tank = gasmix; + // Bookmark if (bookmark) { sample.event.type = SAMPLE_EVENT_BOOKMARK; @@ -1141,82 +1199,76 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); } - if (ISGENIUS(parser->model)) { - // Deco stop / NDL. - if (decostop) { - sample.deco.type = DC_DECO_DECOSTOP; - sample.deco.depth = decodepth; - } else { - sample.deco.type = DC_DECO_NDL; - sample.deco.depth = 0.0; - } - sample.deco.time = decotime * 60; - sample.deco.tts = tts; - if (callback) callback (DC_SAMPLE_DECO, &sample, userdata); - - // Alarms - for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) { - if ((v & 1) == 0) { - continue; - } - - switch (i) { - case ALARM_FAST_ASCENT: - case ALARM_UNCONTROLLED_ASCENT: - sample.event.type = SAMPLE_EVENT_ASCENT; - break; - case ALARM_MISSED_DECO: - case ALARM_DIVE_VIOLATION_DECO: - sample.event.type = SAMPLE_EVENT_CEILING; - break; - default: - sample.event.type = SAMPLE_EVENT_NONE; - break; - } - - if (sample.event.type != SAMPLE_EVENT_NONE) { - sample.event.time = 0; - sample.event.flags = 0; - sample.event.value = 0; - if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); - } - } + // Deco stop / NDL. + if (decostop) { + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.depth = decodepth; + } else { + sample.deco.type = DC_DECO_NDL; + sample.deco.depth = 0.0; } - - offset += parser->samplesize; - nsamples++; - - // Some extra data. - if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) { - if (ISGENIUS(parser->model) && - !mares_genius_isvalid (data + offset, AIRS_SIZE, AIRS_TYPE)) { - ERROR (abstract->context, "Invalid AIRS record."); - return DC_STATUS_DATAFORMAT; + sample.deco.time = decotime * 60; + sample.deco.tts = tts; + if (callback) callback (DC_SAMPLE_DECO, &sample, userdata); + + // Alarms + for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) { + if ((v & 1) == 0) { + continue; } - // Pressure (1/100 bar). - unsigned int pressure = array_uint16_le(data + offset + marker + 0); - if (gasmix < parser->ntanks) { - sample.pressure.tank = gasmix; - sample.pressure.value = pressure / 100.0; - if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); - } else if (pressure != 0) { - WARNING (abstract->context, "Invalid tank with non-zero pressure."); + switch (i) { + case ALARM_FAST_ASCENT: + case ALARM_UNCONTROLLED_ASCENT: + sample.event.type = SAMPLE_EVENT_ASCENT; + break; + case ALARM_MISSED_DECO: + case ALARM_DIVE_VIOLATION_DECO: + sample.event.type = SAMPLE_EVENT_CEILING; + break; + default: + sample.event.type = SAMPLE_EVENT_NONE; + break; } - offset += ISGENIUS(parser->model) ? AIRS_SIZE : 8; + if (sample.event.type != SAMPLE_EVENT_NONE) { + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = 0; + if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata); + } + } + } else if (type == AIRS_TYPE) { + // Pressure (1/100 bar). + unsigned int pressure = array_uint16_le(data + offset + marker + 0); + if (tank < parser->ntanks) { + sample.pressure.tank = tank; + sample.pressure.value = pressure / 100.0; + if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata); + } else if (pressure != 0) { + WARNING (abstract->context, "Invalid tank with non-zero pressure."); } } - } - if (ISGENIUS(parser->model)) { - // Skip the DEND record. - if (!mares_genius_isvalid (data + offset, DEND_SIZE, DEND_TYPE)) { - ERROR (abstract->context, "Invalid DEND record."); - return DC_STATUS_DATAFORMAT; - } - offset += DEND_SIZE; + offset += length; } return DC_STATUS_SUCCESS; } + +static dc_status_t +mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract; + + // Cache the parser data. + dc_status_t rc = mares_iconhd_parser_cache (parser); + if (rc != DC_STATUS_SUCCESS) + return rc; + + if (ISGENIUS(parser->model)) { + return mares_genius_foreach (abstract, callback, userdata); + } else { + return mares_iconhd_foreach (abstract, callback, userdata); + } +} From 6a4f2d334cd6302612ec532329e6573c66d3f1d1 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 31 Jul 2023 19:39:49 +0200 Subject: [PATCH 5/6] Add support for the Mares Sirius (and compatible models) The Mares Sirius uses the same communication protocol as the Genius, except for the fact that it uses a newer BLE version which supports larger data packets. The actual MTU is likely negotiated because we see different sizes like 244 and 182 bytes. We don't have access to this MTU size because not all BLE implementations can expose this information. Unfortunately not only the BLE packet size is variable, but also the size of the higher level data frames (used for downloading the content of the objects) is no longer fixed. The frame size appears to adapt to the BLE MTU size. This is most likely done to reduce the overhead and maximize the throughput. Although each frame ends with an OxEA byte, we can't rely on this knowledge to detect the end of the frame. The END byte is not escaped in the payload, and thus can also appear anywhere in the frame. As a workaround, we rely on the fact that each frame appears to be send as a single BLE packet. The only exception is the ACK byte, which gets send as a separate BLE packet if the command requires parameter data. Compatible models: Quad Ci, Puck 4 and Puck Air 2 Signed-off-by: rmultan --- src/descriptor.c | 7 ++ src/device.c | 2 +- src/mares_iconhd.c | 176 ++++++++++++++++++++++++++++++++++---- src/mares_iconhd.h | 2 +- src/mares_iconhd_parser.c | 10 ++- 5 files changed, 177 insertions(+), 20 deletions(-) diff --git a/src/descriptor.c b/src/descriptor.c index f23c93dd..f47cb479 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -307,6 +307,10 @@ static const dc_descriptor_t g_descriptors[] = { {"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Horizon", DC_FAMILY_MARES_ICONHD , 0x2C, DC_TRANSPORT_SERIAL, NULL}, + {"Mares", "Puck Air 2", DC_FAMILY_MARES_ICONHD , 0x2D, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Sirius", DC_FAMILY_MARES_ICONHD , 0x2F, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Quad Ci", DC_FAMILY_MARES_ICONHD , 0x31, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Puck 4", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares}, /* Heinrichs Weikamp */ {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL}, @@ -715,6 +719,9 @@ dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const vo static const char * const bluetooth[] = { "Mares bluelink pro", "Mares Genius", + "Sirius", + "Quad Ci", + "Puck4", }; if (transport == DC_TRANSPORT_BLE) { diff --git a/src/device.c b/src/device.c index 64431595..f1a0ee7a 100644 --- a/src/device.c +++ b/src/device.c @@ -175,7 +175,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = mares_darwin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_MARES_ICONHD: - rc = mares_iconhd_device_open (&device, context, iostream); + rc = mares_iconhd_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_HW_OSTC: rc = hw_ostc_device_open (&device, context, iostream); diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 2e8aaad3..5791b053 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -48,6 +48,10 @@ #define SMARTAIR 0x24 #define QUAD 0x29 #define HORIZON 0x2C +#define PUCKAIR2 0x2D +#define SIRIUS 0x2F +#define QUADCI 0x31 +#define PUCK4 0x35 #define ISSMART(model) ( \ (model) == SMART || \ @@ -56,10 +60,25 @@ #define ISGENIUS(model) ( \ (model) == GENIUS || \ - (model) == HORIZON) + (model) == HORIZON || \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) + +#define ISSIRIUS(model) ( \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) #define MAXRETRIES 4 +#define MAXPACKET 244 + +#define FIXED 0 +#define VARIABLE 1 + #define ACK 0xAA #define EOF 0xEA #define XOR 0xA5 @@ -104,6 +123,7 @@ typedef struct mares_iconhd_device_t { unsigned char version[140]; unsigned int model; unsigned int packetsize; + unsigned int ble; } mares_iconhd_device_t; static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); @@ -171,6 +191,10 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) {"Smart Air", SMARTAIR}, {"Quad", QUAD}, {"Horizon", HORIZON}, + {"Puck Air 2", PUCKAIR2}, + {"Sirius", SIRIUS}, + {"Quad Ci", QUADCI}, + {"Puck4", PUCK4}, }; // Check the product name in the version packet against the list @@ -187,10 +211,11 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) } static dc_status_t -mares_iconhd_packet (mares_iconhd_device_t *device, +mares_iconhd_packet_fixed (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, - unsigned char answer[], unsigned int asize) + unsigned char answer[], unsigned int asize, + unsigned int *actual) { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; @@ -262,15 +287,120 @@ mares_iconhd_packet (mares_iconhd_device_t *device, return DC_STATUS_PROTOCOL; } + if (actual) { + *actual = asize; + } + return DC_STATUS_SUCCESS; } static dc_status_t -mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize) +mares_iconhd_packet_variable (mares_iconhd_device_t *device, + unsigned char cmd, + const unsigned char data[], unsigned int size, + unsigned char answer[], unsigned int asize, + unsigned int *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[MAXPACKET] = {0}; + size_t length = 0; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + // Send the command header to the dive computer. + const unsigned char command[2] = { + cmd, cmd ^ XOR, + }; + status = dc_iostream_write (device->iostream, command, sizeof(command), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command header."); + return status; + } + + // Read either the entire data packet (if there is no command data to send), + // or only the header byte (if there is also command data to send). + status = dc_iostream_read (device->iostream, packet, sizeof (packet), &length); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet header."); + return status; + } + + if (size) { + // Send the command payload to the dive computer. + status = dc_iostream_write (device->iostream, data, size, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command data."); + return status; + } + + // Read the data packet. + size_t len = 0; + status = dc_iostream_read (device->iostream, packet + length, sizeof (packet) - length, &len); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet data."); + return status; + } + + length += len; + } + + if (length < 2 || length - 2 > asize) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length); + return DC_STATUS_PROTOCOL; + } + + // Verify the header byte. + if (packet[0] != ACK) { + ERROR (abstract->context, "Unexpected packet header byte (%02x).", packet[0]); + return DC_STATUS_PROTOCOL; + } + + // Verify the trailer byte. + if (packet[length - 1] != END) { + ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", packet[length - 1]); + return DC_STATUS_PROTOCOL; + } + + if (actual == NULL) { + // Verify the actual length. + if (length - 2 != asize) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length - 2); + return DC_STATUS_PROTOCOL; + } + } else { + // Return the actual length. + *actual = length - 2; + } + + memcpy (answer, packet + 1, length - 2); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +mares_iconhd_packet (mares_iconhd_device_t *device, + unsigned char cmd, + const unsigned char data[], unsigned int size, + unsigned char answer[], unsigned int asize, + unsigned int *actual) +{ + dc_transport_t transport = dc_iostream_get_transport (device->iostream); + + if (transport == DC_TRANSPORT_BLE && device->ble == VARIABLE) { + return mares_iconhd_packet_variable (device, cmd, data, size, answer, asize, actual); + } else { + return mares_iconhd_packet_fixed (device, cmd, data, size, answer, asize, actual); + } +} + +static dc_status_t +mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *actual) { unsigned int nretries = 0; dc_status_t rc = DC_STATUS_SUCCESS; - while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize)) != DC_STATUS_SUCCESS) { + while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize, actual)) != DC_STATUS_SUCCESS) { // Automatically discard a corrupted packet, // and request a new one. if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) @@ -294,7 +424,9 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; dc_transport_t transport = dc_iostream_get_transport (device->iostream); - const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? 124 : 504; + const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? + (device->ble == VARIABLE ? MAXPACKET - 3 : 124) : + 504; // Update and emit a progress event. unsigned int initial = 0; @@ -312,7 +444,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro subindex & 0xFF }; memset (cmd_init + 6, 0x00, sizeof(cmd_init) - 6); - status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init)); + status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to transfer the init packet."); return status; @@ -366,13 +498,21 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } // Transfer the segment packet. + unsigned int length = 0; unsigned char rsp_segment[1 + 504]; - status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1); + status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1, &length); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to transfer the segment packet."); return status; } + if (length < 1) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + length--; + // Verify the packet header. if ((rsp_segment[0] & 0xF0) >> 4 != toggle) { ERROR (abstract->context, "Unexpected packet header (%02x).", rsp_segment[0]); @@ -380,12 +520,12 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } // Append the payload to the output buffer. - if (!dc_buffer_append (buffer, rsp_segment + 1, len)) { + if (!dc_buffer_append (buffer, rsp_segment + 1, length)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } - nbytes += len; + nbytes += length; npackets++; // Update and emit the progress events. @@ -399,7 +539,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } dc_status_t -mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = NULL; @@ -422,9 +562,10 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ memset (device->version, 0, sizeof (device->version)); device->model = 0; device->packetsize = 0; + device->ble = ISSIRIUS(model) ? VARIABLE : FIXED; // Create the packet stream. - if (transport == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { status = dc_packet_open (&device->iostream, context, iostream, 244, 20); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to create the packet stream."); @@ -467,7 +608,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ // Send the version command. status = mares_iconhd_transfer (device, CMD_VERSION, NULL, 0, - device->version, sizeof (device->version)); + device->version, sizeof (device->version), NULL); if (status != DC_STATUS_SUCCESS) { goto error_free_iostream; } @@ -479,7 +620,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ unsigned int memsize = 0; if (device->model == QUAD) { unsigned char rsp_flash[4] = {0}; - status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash)); + status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash), NULL); if (status != DC_STATUS_SUCCESS) { WARNING (context, "Failed to read the flash memory size."); } else { @@ -550,7 +691,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ error_free_iostream: - if (transport == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { dc_iostream_close (device->iostream); } error_free: @@ -563,9 +704,10 @@ static dc_status_t mares_iconhd_device_close (dc_device_t *abstract) { mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; + dc_transport_t transport = dc_iostream_get_transport (device->iostream); // Close the packet stream. - if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { return dc_iostream_close (device->iostream); } @@ -613,7 +755,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned (len >> 8) & 0xFF, (len >> 16) & 0xFF, (len >> 24) & 0xFF}; - rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len); + rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len, NULL); if (rc != DC_STATUS_SUCCESS) return rc; diff --git a/src/mares_iconhd.h b/src/mares_iconhd.h index c055012d..667a180e 100644 --- a/src/mares_iconhd.h +++ b/src/mares_iconhd.h @@ -32,7 +32,7 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); +mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); dc_status_t mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model, unsigned int serial); diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index a9d87dc4..c38d3834 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -47,6 +47,10 @@ #define QUADAIR 0x23 #define SMARTAIR 0x24 #define HORIZON 0x2C +#define PUCKAIR2 0x2D +#define SIRIUS 0x2F +#define QUADCI 0x31 +#define PUCK4 0x35 #define ISSMART(model) ( \ (model) == SMART || \ @@ -55,7 +59,11 @@ #define ISGENIUS(model) ( \ (model) == GENIUS || \ - (model) == HORIZON) + (model) == HORIZON || \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) #define NGASMIXES_ICONHD 3 #define NGASMIXES_GENIUS 5 From aa3d0bca945263db6bdd3181f87d94da28b61ed8 Mon Sep 17 00:00:00 2001 From: rmultan Date: Wed, 24 Jul 2024 08:44:21 +0200 Subject: [PATCH 6/6] Fix macro usage after cherry pick Signed-off-by: rmultan --- src/mares_iconhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 5791b053..03b00e51 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -358,7 +358,7 @@ mares_iconhd_packet_variable (mares_iconhd_device_t *device, } // Verify the trailer byte. - if (packet[length - 1] != END) { + if (packet[length - 1] != EOF) { ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", packet[length - 1]); return DC_STATUS_PROTOCOL; }