Merge git://github.com/libdivecomputer/libdivecomputer into Subsurface-NG

Merge with upstream from Jef:

 - ignore empty pressure samples from OSTC

 - skip empty Oceanic logbook entries

 - update Ratio parsing

* 'master' of git://github.com/libdivecomputer/libdivecomputer:
  Ignore zero tank pressure values
  Add filters for BLE communication
  Skip empty logbook entries
  Add clock synchronization support
  Use symbolic constants for the commands
  Don't pass a NULL pointer to memcpy
  Update the list of the Ratio dive computers
This commit is contained in:
Linus Torvalds 2019-03-12 17:00:19 -07:00
commit d0ec5cf760
5 changed files with 208 additions and 36 deletions

View File

@ -314,20 +314,32 @@ static const dc_descriptor_t g_descriptors[] = {
{"DiveSystem", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x09, DC_TRANSPORT_SERIAL, NULL},
{"DiveSystem", "iDive X3M", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0A, DC_TRANSPORT_SERIAL, NULL},
{"DiveSystem", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x0B, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro ", DC_FAMILY_DIVESYSTEM_IDIVE, 0x21, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x22, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x23, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x24, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x25, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x26, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro Fancy",DC_FAMILY_DIVESYSTEM_IDIVE, 0x31, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x32, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x33, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x34, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro Tech+",DC_FAMILY_DIVESYSTEM_IDIVE, 0x35, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M Pro Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x36, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x40, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x41, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x42, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x43, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x44, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x45, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x46, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x50, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Fancy",DC_FAMILY_DIVESYSTEM_IDIVE, 0x51, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x52, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x53, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x54, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x55, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Tech+",DC_FAMILY_DIVESYSTEM_IDIVE, 0x55, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive Color Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x56, DC_TRANSPORT_SERIAL, NULL},
{"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000, DC_TRANSPORT_SERIAL, NULL},
{"Seac", "Guru", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1002, DC_TRANSPORT_SERIAL, NULL},
/* Cochran Commander */
@ -411,11 +423,17 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata)
{0x2e6c, 0x3211}, // G2 Console
{0xc251, 0x2006}, // Aladin Square
};
static const char *bluetooth[] = {
"G2",
"Aladin",
};
if (transport == DC_TRANSPORT_IRDA) {
return dc_filter_internal_name ((const char *) userdata, irda, C_ARRAY_SIZE(irda));
} else if (transport == DC_TRANSPORT_USBHID) {
return dc_filter_internal_usb ((const dc_usb_desc_t *) userdata, usbhid, C_ARRAY_SIZE(usbhid));
} else if (transport == DC_TRANSPORT_BLE) {
return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth));
}
return 1;
@ -427,9 +445,15 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata)
{0x1493, 0x0030}, // Eon Steel
{0x1493, 0x0033}, // Eon Core
};
static const char *bluetooth[] = {
"EON Steel",
"EON Core",
};
if (transport == DC_TRANSPORT_USBHID) {
return dc_filter_internal_usb ((const dc_usb_desc_t *) userdata, usbhid, C_ARRAY_SIZE(usbhid));
} else if (transport == DC_TRANSPORT_BLE) {
return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth));
}
return 1;
@ -437,7 +461,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata)
static int dc_filter_hw (dc_transport_t transport, const void *userdata)
{
if (transport == DC_TRANSPORT_BLUETOOTH) {
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
return strncasecmp ((const char *) userdata, "OSTC", 4) == 0 ||
strncasecmp ((const char *) userdata, "FROG", 4) == 0;
} else if (transport == DC_TRANSPORT_SERIAL) {
@ -457,7 +481,7 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata)
"Teric",
};
if (transport == DC_TRANSPORT_BLUETOOTH) {
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth));
} else if (transport == DC_TRANSPORT_SERIAL) {
return dc_filter_internal_rfcomm ((const char *) userdata);

View File

@ -28,12 +28,11 @@
#include "checksum.h"
#include "array.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define ISINSTANCE(device) dc_device_isinstance((device), &divesystem_idive_device_vtable)
#define IX3M_EASY 0x22
#define IX3M_DEEP 0x23
#define IX3M_TEC 0x24
#define IX3M_REB 0x25
#define ISIX3M(model) ((model) >= 0x21)
#define MAXRETRIES 9
@ -42,6 +41,17 @@
#define ACK 0x06
#define NAK 0x15
#define CMD_IDIVE_ID 0x10
#define CMD_IDIVE_RANGE 0x98
#define CMD_IDIVE_HEADER 0xA0
#define CMD_IDIVE_SAMPLE 0xA8
#define CMD_IX3M_ID 0x11
#define CMD_IX3M_RANGE 0x78
#define CMD_IX3M_HEADER 0x79
#define CMD_IX3M_SAMPLE 0x7A
#define CMD_IX3M_TIMESYNC 0x13
#define ERR_INVALID_CMD 0x10
#define ERR_INVALID_LENGTH 0x20
#define ERR_INVALID_DATA 0x30
@ -53,6 +63,10 @@
#define NSTEPS 1000
#define STEP(i,n) (NSTEPS * (i) / (n))
#define EPOCH 1199145600 /* 2008-01-01 00:00:00 */
#define TZ_IDX_UNCHANGED 0xFF
typedef struct divesystem_idive_command_t {
unsigned char cmd;
unsigned int size;
@ -75,6 +89,7 @@ typedef struct divesystem_idive_device_t {
static dc_status_t divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
static const dc_device_vtable_t divesystem_idive_device_vtable = {
sizeof(divesystem_idive_device_t),
@ -84,31 +99,31 @@ static const dc_device_vtable_t divesystem_idive_device_vtable = {
NULL, /* write */
NULL, /* dump */
divesystem_idive_device_foreach, /* foreach */
NULL, /* timesync */
divesystem_idive_device_timesync, /* timesync */
NULL /* close */
};
static const divesystem_idive_commands_t idive = {
{0x10, 0x0A},
{0x98, 0x04},
{0xA0, 0x32},
{0xA8, 0x2A},
{CMD_IDIVE_ID, 0x0A},
{CMD_IDIVE_RANGE, 0x04},
{CMD_IDIVE_HEADER, 0x32},
{CMD_IDIVE_SAMPLE, 0x2A},
1,
};
static const divesystem_idive_commands_t ix3m = {
{0x11, 0x1A},
{0x78, 0x04},
{0x79, 0x36},
{0x7A, 0x36},
{CMD_IX3M_ID, 0x1A},
{CMD_IX3M_RANGE, 0x04},
{CMD_IX3M_HEADER, 0x36},
{CMD_IX3M_SAMPLE, 0x36},
1,
};
static const divesystem_idive_commands_t ix3m_apos4 = {
{0x11, 0x1A},
{0x78, 0x04},
{0x79, 0x36},
{0x7A, 0x40},
{CMD_IX3M_ID, 0x1A},
{CMD_IX3M_RANGE, 0x04},
{CMD_IX3M_HEADER, 0x36},
{CMD_IX3M_SAMPLE, 0x40},
3,
};
@ -322,7 +337,9 @@ divesystem_idive_packet (divesystem_idive_device_t *device, const unsigned char
goto error;
}
memcpy(answer, packet + 1, length - 2);
if (length > 2) {
memcpy (answer, packet + 1, length - 2);
}
error:
if (errorcode) {
@ -374,7 +391,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
unsigned int errcode = 0;
const divesystem_idive_commands_t *commands = &idive;
if (device->model >= IX3M_EASY) {
if (ISIX3M(device->model)) {
commands = &ix3m;
}
@ -400,7 +417,7 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
vendor.size = commands->id.size;
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
if (device->model >= IX3M_EASY) {
if (ISIX3M(device->model)) {
// Detect the APOS4 firmware.
unsigned int apos4 = (devinfo.firmware / 10000000) >= 4;
if (apos4) {
@ -514,3 +531,124 @@ divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
return DC_STATUS_SUCCESS;
}
static dc_status_t
divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
{
dc_status_t rc = DC_STATUS_SUCCESS;
divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract;
unsigned int errcode = 0;
static const signed char tz_array[] = {
-12, 0, /* UTC-12 */
-11, 0, /* UTC-11 */
-10, 0, /* UTC-10 */
-9, 30, /* UTC-9:30 */
-9, 0, /* UTC-9 */
-8, 0, /* UTC-8 */
-7, 0, /* UTC-7 */
-6, 0, /* UTC-6 */
-5, 0, /* UTC-5 */
-4, 30, /* UTC-4:30 */
-4, 0, /* UTC-4 */
-3, 30, /* UTC-3:30 */
-3, 0, /* UTC-3 */
-2, 0, /* UTC-2 */
-1, 0, /* UTC-1 */
0, 0, /* UTC */
1, 0, /* UTC+1 */
2, 0, /* UTC+2 */
3, 0, /* UTC+3 */
3, 30, /* UTC+3:30 */
4, 0, /* UTC+4 */
4, 30, /* UTC+4:30 */
5, 0, /* UTC+5 */
5, 30, /* UTC+5:30 */
5, 45, /* UTC+5:45 */
6, 0, /* UTC+6 */
6, 30, /* UTC+6:30 */
7, 0, /* UTC+7 */
8, 0, /* UTC+8 */
8, 45, /* UTC+8:45 */
9, 0, /* UTC+9 */
9, 30, /* UTC+9:30 */
9, 45, /* UTC+9:45 */
10, 0, /* UTC+10 */
10, 30, /* UTC+10:30 */
11, 0, /* UTC+11 */
11, 30, /* UTC+11:30 */
12, 0, /* UTC+12 */
12, 45, /* UTC+12:45 */
13, 0, /* UTC+13 */
13, 45, /* UTC+13:45 */
14, 0 /* UTC+14 */
};
if (!ISIX3M(device->model)) {
return DC_STATUS_UNSUPPORTED;
}
if (datetime == NULL) {
ERROR (abstract->context, "Invalid parameter specified.");
return DC_STATUS_INVALIDARGS;
}
// Get the UTC timestamp.
dc_ticks_t timestamp = dc_datetime_mktime(datetime);
if (timestamp == -1) {
ERROR (abstract->context, "Invalid date/time value specified.");
return DC_STATUS_INVALIDARGS;
}
// Adjust the epoch.
timestamp -= EPOCH;
// Find the timezone index.
size_t tz_idx = C_ARRAY_SIZE(tz_array);
for (size_t i = 0; i < C_ARRAY_SIZE(tz_array); i += 2) {
int timezone = tz_array[i] * 3600;
if (timezone < 0) {
timezone -= tz_array[i + 1] * 60;
} else {
timezone += tz_array[i + 1] * 60;
}
if (timezone == datetime->timezone) {
tz_idx = i;
break;
}
}
if (tz_idx >= C_ARRAY_SIZE(tz_array)) {
ERROR (abstract->context, "Invalid timezone value specified.");
return DC_STATUS_INVALIDARGS;
}
// Adjust the timezone index.
tz_idx /= 2;
// Send the command.
unsigned char command[] = {
CMD_IX3M_TIMESYNC,
(timestamp >> 0) & 0xFF,
(timestamp >> 8) & 0xFF,
(timestamp >> 16) & 0xFF,
(timestamp >> 24) & 0xFF,
tz_idx, // Home timezone
TZ_IDX_UNCHANGED}; // Travel timezone
rc = divesystem_idive_transfer (device, command, sizeof(command), NULL, 0, &errcode);
if (rc != DC_STATUS_SUCCESS) {
if (errcode == ERR_INVALID_LENGTH || errcode == ERR_INVALID_DATA) {
// Fallback to the variant without the second timezone if the
// firmware doesn't support two timezones (ERR_INVALID_LENGTH) or
// leaving the timezone unchanged (ERR_INVALID_DATA).
rc = divesystem_idive_transfer (device, command, sizeof(command) - 1, NULL, 0, &errcode);
if (rc != DC_STATUS_SUCCESS) {
return rc;
}
} else {
return rc;
}
}
return DC_STATUS_SUCCESS;
}

View File

@ -30,10 +30,7 @@
#define ISINSTANCE(parser) dc_device_isinstance((parser), &divesystem_idive_parser_vtable)
#define IX3M_EASY 0x22
#define IX3M_DEEP 0x23
#define IX3M_TEC 0x24
#define IX3M_REB 0x25
#define ISIX3M(model) ((model) >= 0x21)
#define SZ_HEADER_IDIVE 0x32
#define SZ_SAMPLE_IDIVE 0x2A
@ -103,7 +100,7 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
// Set the default values.
parser->model = model;
if (model >= IX3M_EASY) {
if (ISIX3M(model)) {
parser->headersize = SZ_HEADER_IX3M;
} else {
parser->headersize = SZ_HEADER_IDIVE;
@ -206,7 +203,7 @@ divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *date
// Detect the APOS4 firmware.
unsigned int firmware = 0;
unsigned int apos4 = 0;
if (parser->model >= IX3M_EASY) {
if (ISIX3M(parser->model)) {
firmware = array_uint32_le(abstract->data + 0x2A);
apos4 = (firmware / 10000000) >= 4;
} else {
@ -297,7 +294,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
tank->gasmix = DC_GASMIX_UNKNOWN;
break;
case DC_FIELD_ATMOSPHERIC:
if (parser->model >= IX3M_EASY) {
if (ISIX3M(parser->model)) {
*((double *) value) = array_uint16_le (data + 11) / 10000.0;
} else {
*((double *) value) = array_uint16_le (data + 11) / 1000.0;
@ -363,7 +360,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
unsigned int apos4 = 0;
unsigned int nsamples = array_uint16_le (data + 1);
unsigned int samplesize = SZ_SAMPLE_IDIVE;
if (parser->model >= IX3M_EASY) {
if (ISIX3M(parser->model)) {
// Detect the APOS4 firmware.
firmware = array_uint32_le(data + 0x2A);
apos4 = (firmware / 10000000) >= 4;

View File

@ -1032,9 +1032,11 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
break;
case 6: // Tank pressure
value = array_uint16_le (data + offset);
if (value != 0) {
sample.pressure.tank = tank;
sample.pressure.value = value / 10.0;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
}
break;
default: // Not yet used.
break;

View File

@ -304,8 +304,7 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr
// fix this here.
if (array_isequal (logbooks + offset, layout->rb_logbook_entry_size, 0xFF)) {
WARNING (abstract->context, "Uninitialized logbook entries detected!");
offset += layout->rb_logbook_entry_size;
break;
continue;
}
// Compare the fingerprint to identify previously downloaded entries.
@ -364,6 +363,12 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
// Move to the start of the current entry.
entry -= layout->rb_logbook_entry_size;
// Skip uninitialized entries.
if (array_isequal (logbooks + entry, layout->rb_logbook_entry_size, 0xFF)) {
WARNING (abstract->context, "Skipping uninitialized logbook entry!");
continue;
}
// 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);
@ -442,6 +447,12 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
// Move to the start of the current entry.
entry -= layout->rb_logbook_entry_size;
// Skip uninitialized entries.
if (array_isequal (logbooks + entry, layout->rb_logbook_entry_size, 0xFF)) {
WARNING (abstract->context, "Skipping uninitialized logbook entry!");
continue;
}
// 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);