diff --git a/src/descriptor.c b/src/descriptor.c index 1c533db..545dcf7 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -313,20 +313,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 */ diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index f827b52..1119b23 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -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; +} diff --git a/src/divesystem_idive_parser.c b/src/divesystem_idive_parser.c index adfb48a..66f77dc 100644 --- a/src/divesystem_idive_parser.c +++ b/src/divesystem_idive_parser.c @@ -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;