From 12f44f341018a921d48fd43574340df1b584c1ca Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 10 Feb 2023 21:03:39 +0100 Subject: [PATCH 01/25] Add an extra parameter for the xorout value This change allows to calculate some more variants of the CRC-CCITT algorithm with a single function. --- src/checksum.c | 4 ++-- src/checksum.h | 2 +- src/cressi_goa.c | 6 +++--- src/cressi_leonardo.c | 6 +++--- src/divesystem_idive.c | 4 ++-- src/liquivision_lynx.c | 2 +- src/mares_iconhd_parser.c | 2 +- src/oceans_s1.c | 2 +- src/reefnet_sensuspro.c | 4 ++-- src/reefnet_sensusultra.c | 4 ++-- src/seac_screen.c | 8 ++++---- src/seac_screen_parser.c | 6 +++--- 12 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/checksum.c b/src/checksum.c index 9d06630..6dcdcd5 100644 --- a/src/checksum.c +++ b/src/checksum.c @@ -69,7 +69,7 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char unsigned short -checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init) +checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout) { static const unsigned short crc_ccitt_table[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, @@ -110,7 +110,7 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh for (unsigned int i = 0; i < size; ++i) crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]]; - return crc; + return crc ^ xorout; } unsigned int diff --git a/src/checksum.h b/src/checksum.h index c8a9001..39da07c 100644 --- a/src/checksum.h +++ b/src/checksum.h @@ -39,7 +39,7 @@ unsigned char checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char init); unsigned short -checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init); +checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); unsigned int checksum_crc32 (const unsigned char data[], unsigned int size); diff --git a/src/cressi_goa.c b/src/cressi_goa.c index e414743..9df10e8 100644 --- a/src/cressi_goa.c +++ b/src/cressi_goa.c @@ -93,7 +93,7 @@ cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const un if (size) { memcpy (packet + 5, data, size); } - crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000); + crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000, 0x0000); packet[5 + size + 0] = (crc ) & 0xFF; // Low packet[5 + size + 1] = (crc >> 8) & 0xFF; // High packet[5 + size + 2] = TRAILER; @@ -155,7 +155,7 @@ cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], un // Verify the checksum of the packet. unsigned short crc = array_uint16_le (packet + length + 5); - unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000); + unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; @@ -203,7 +203,7 @@ cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc // Verify the checksum of the packet. unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2); - unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000); + unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c index f331537..dd941da 100644 --- a/src/cressi_leonardo.c +++ b/src/cressi_leonardo.c @@ -84,7 +84,7 @@ cressi_leonardo_make_ascii (const unsigned char raw[], unsigned int rsize, unsig array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize); // Checksum - unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff); + unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff, 0x0000); unsigned char checksum[] = { (crc >> 8) & 0xFF, // High (crc ) & 0xFF}; // Low @@ -129,7 +129,7 @@ cressi_leonardo_packet (cressi_leonardo_device_t *device, const unsigned char co // Verify the checksum of the packet. unsigned short crc = array_uint16_be (checksum); - unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff); + unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; @@ -372,7 +372,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) // Verify the checksum. unsigned int csum1 = array_uint16_be (checksum); - unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff); + unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff, 0x0000); if (csum1 != csum2) { ERROR (abstract->context, "Unexpected answer bytes."); return DC_STATUS_PROTOCOL; diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index 0410cd2..4a0bca3 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -231,7 +231,7 @@ divesystem_idive_send (divesystem_idive_device_t *device, const unsigned char co packet[0] = START; packet[1] = csize; memcpy(packet + 2, command, csize); - crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff); + crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff, 0x0000); packet[csize + 2] = (crc >> 8) & 0xFF; packet[csize + 3] = (crc ) & 0xFF; @@ -292,7 +292,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe // Verify the checksum. unsigned short crc = array_uint16_be (packet + len + 2); - unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff); + unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected packet checksum."); return DC_STATUS_PROTOCOL; diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index a0d8e0a..e7ad5a4 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -158,7 +158,7 @@ liquivision_lynx_recv (liquivision_lynx_device_t *device, unsigned char data[], // Verify the checksum. unsigned short crc = array_uint16_be (packet + 1 + size); - unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff); + unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum (%04x %04x).", crc, ccrc); return DC_STATUS_PROTOCOL; diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index 184bf92..600eaa9 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -296,7 +296,7 @@ mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type } unsigned short crc = array_uint16_le(data + size - 6); - unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000); + unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000); if (crc != ccrc) { return 0; } diff --git a/src/oceans_s1.c b/src/oceans_s1.c index e26979e..125d090 100644 --- a/src/oceans_s1.c +++ b/src/oceans_s1.c @@ -302,7 +302,7 @@ oceans_s1_xmodem_packet (oceans_s1_device_t *device, unsigned char seq, unsigned } unsigned short crc = array_uint16_be (packet + nbytes - 2); - unsigned short ccrc = checksum_crc16_ccitt (packet + 3, nbytes - 5, 0x0000); + unsigned short ccrc = checksum_crc16_ccitt (packet + 3, nbytes - 5, 0x0000, 0x0000); if (crc != ccrc) { ERROR (device->base.context, "Unexpected answer checksum (%04x %04x).", crc, ccrc); return DC_STATUS_PROTOCOL; diff --git a/src/reefnet_sensuspro.c b/src/reefnet_sensuspro.c index 835fbcb..27c2fdb 100644 --- a/src/reefnet_sensuspro.c +++ b/src/reefnet_sensuspro.c @@ -177,7 +177,7 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device) // Verify the checksum of the handshake packet. unsigned short crc = array_uint16_le (handshake + SZ_HANDSHAKE); - unsigned short ccrc = checksum_crc16_ccitt (handshake, SZ_HANDSHAKE, 0xffff); + unsigned short ccrc = checksum_crc16_ccitt (handshake, SZ_HANDSHAKE, 0xffff, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; @@ -280,7 +280,7 @@ reefnet_sensuspro_device_dump (dc_device_t *abstract, dc_buffer_t *buffer) } unsigned short crc = array_uint16_le (answer + SZ_MEMORY); - unsigned short ccrc = checksum_crc16_ccitt (answer, SZ_MEMORY, 0xffff); + unsigned short ccrc = checksum_crc16_ccitt (answer, SZ_MEMORY, 0xffff, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; diff --git a/src/reefnet_sensusultra.c b/src/reefnet_sensusultra.c index 6b059d6..bfe1c6a 100644 --- a/src/reefnet_sensusultra.c +++ b/src/reefnet_sensusultra.c @@ -224,7 +224,7 @@ reefnet_sensusultra_packet (reefnet_sensusultra_device_t *device, unsigned char // Verify the checksum of the packet. unsigned short crc = array_uint16_le (data + size - 2); - unsigned short ccrc = checksum_crc16_ccitt (data + header, size - header - 2, 0xffff); + unsigned short ccrc = checksum_crc16_ccitt (data + header, size - header - 2, 0xffff, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected answer checksum."); return DC_STATUS_PROTOCOL; @@ -477,7 +477,7 @@ reefnet_sensusultra_device_write_user (dc_device_t *abstract, const unsigned cha } // Send the checksum to the device. - unsigned short crc = checksum_crc16_ccitt (data, SZ_USER, 0xffff); + unsigned short crc = checksum_crc16_ccitt (data, SZ_USER, 0xffff, 0x0000); rc = reefnet_sensusultra_send_ushort (device, crc); if (rc != DC_STATUS_SUCCESS) return rc; diff --git a/src/seac_screen.c b/src/seac_screen.c index e9b5366..9b3796f 100644 --- a/src/seac_screen.c +++ b/src/seac_screen.c @@ -115,7 +115,7 @@ seac_screen_send (seac_screen_device_t *device, unsigned short cmd, const unsign if (size) { memcpy (packet + 5, data, size); } - crc = checksum_crc16_ccitt (packet, size + 5, 0xFFFF); + crc = checksum_crc16_ccitt (packet, size + 5, 0xFFFF, 0x0000); packet[size + 5] = (crc >> 8) & 0xFF; packet[size + 6] = (crc ) & 0xFF; @@ -165,7 +165,7 @@ seac_screen_receive (seac_screen_device_t *device, unsigned short cmd, unsigned // Verify the checksum. unsigned short crc = array_uint16_be (packet + 1 + length - 2); - unsigned short ccrc = checksum_crc16_ccitt (packet, 1 + length - 2, 0xFFFF); + unsigned short ccrc = checksum_crc16_ccitt (packet, 1 + length - 2, 0xFFFF, 0x0000); if (crc != ccrc) { ERROR (abstract->context, "Unexpected packet checksum (%04x %04x).", crc, ccrc); return DC_STATUS_PROTOCOL; @@ -486,8 +486,8 @@ seac_screen_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); // Check the header checksums. - if (checksum_crc16_ccitt (logbook[i].header, SZ_HEADER / 2, 0xFFFF) != 0 || - checksum_crc16_ccitt (logbook[i].header + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF) != 0) { + if (checksum_crc16_ccitt (logbook[i].header, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0 || + checksum_crc16_ccitt (logbook[i].header + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0) { ERROR (abstract->context, "Unexpected header checksum."); status = DC_STATUS_DATAFORMAT; goto error_free_logbook; diff --git a/src/seac_screen_parser.c b/src/seac_screen_parser.c index 8c588d2..c4a855c 100644 --- a/src/seac_screen_parser.c +++ b/src/seac_screen_parser.c @@ -283,8 +283,8 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t if (abstract->size < SZ_HEADER) return DC_STATUS_DATAFORMAT; - if (checksum_crc16_ccitt (data, SZ_HEADER / 2, 0xFFFF) != 0 || - checksum_crc16_ccitt (data + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF) != 0) { + if (checksum_crc16_ccitt (data, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0 || + checksum_crc16_ccitt (data + SZ_HEADER / 2, SZ_HEADER / 2, 0xFFFF, 0x0000) != 0) { ERROR (abstract->context, "Unexpected header checksum."); return DC_STATUS_DATAFORMAT; } @@ -303,7 +303,7 @@ seac_screen_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t while (offset + SZ_SAMPLE <= size) { dc_sample_value_t sample = {0}; - if (checksum_crc16_ccitt (data + offset, SZ_SAMPLE, 0xFFFF) != 0) { + if (checksum_crc16_ccitt (data + offset, SZ_SAMPLE, 0xFFFF, 0x0000) != 0) { ERROR (abstract->context, "Unexpected sample checksum."); return DC_STATUS_DATAFORMAT; } From d327aea6ff41c748dfb9e590ca32ed1bf433040d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 10 Feb 2023 21:06:51 +0100 Subject: [PATCH 02/25] Add a reflected variant of the 16-bit CRC-CCITT The reflected variant of a CRC does reverse the bits of each input byte and requires a different lookup table for an efficient implementation. --- src/checksum.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/checksum.h | 3 +++ 2 files changed, 50 insertions(+) diff --git a/src/checksum.c b/src/checksum.c index 6dcdcd5..76f184e 100644 --- a/src/checksum.c +++ b/src/checksum.c @@ -113,6 +113,53 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh return crc ^ xorout; } + +unsigned short +checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout) +{ + static const unsigned short crc_ccitt_table[] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 + }; + + unsigned short crc = init; + for (unsigned int i = 0; i < size; ++i) + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ data[i]) & 0xff]; + + return crc ^ xorout; +} + + unsigned int checksum_crc32 (const unsigned char data[], unsigned int size) { diff --git a/src/checksum.h b/src/checksum.h index 39da07c..c88db86 100644 --- a/src/checksum.h +++ b/src/checksum.h @@ -41,6 +41,9 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char unsigned short checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); +unsigned short +checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); + unsigned int checksum_crc32 (const unsigned char data[], unsigned int size); From 00033e4af0fbe906a7a8a1cca7b1ad1d4d619769 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 10 Feb 2023 21:08:28 +0100 Subject: [PATCH 03/25] Rename the 32-bit CRC functions The new names make it easier to identify the normal and reflected variant of the CRC function. --- src/checksum.c | 4 ++-- src/checksum.h | 4 ++-- src/liquivision_lynx.c | 2 +- src/suunto_eonsteel.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/checksum.c b/src/checksum.c index 76f184e..b271d04 100644 --- a/src/checksum.c +++ b/src/checksum.c @@ -161,7 +161,7 @@ checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned s unsigned int -checksum_crc32 (const unsigned char data[], unsigned int size) +checksum_crc32r (const unsigned char data[], unsigned int size) { static const unsigned int crc_table[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, @@ -206,7 +206,7 @@ checksum_crc32 (const unsigned char data[], unsigned int size) } unsigned int -checksum_crc32b (const unsigned char data[], unsigned int size) +checksum_crc32 (const unsigned char data[], unsigned int size) { static const unsigned int crc_table[] = { 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, diff --git a/src/checksum.h b/src/checksum.h index c88db86..5c91d0a 100644 --- a/src/checksum.h +++ b/src/checksum.h @@ -45,10 +45,10 @@ unsigned short checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); unsigned int -checksum_crc32 (const unsigned char data[], unsigned int size); +checksum_crc32r (const unsigned char data[], unsigned int size); unsigned int -checksum_crc32b (const unsigned char data[], unsigned int size); +checksum_crc32 (const unsigned char data[], unsigned int size); #ifdef __cplusplus } diff --git a/src/liquivision_lynx.c b/src/liquivision_lynx.c index e7ad5a4..b755955 100644 --- a/src/liquivision_lynx.c +++ b/src/liquivision_lynx.c @@ -528,7 +528,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb memcpy (header + 0, device->info + 2, 4); memcpy (header + 4, logbook + offset + 4, headersize - 4); unsigned int crc = array_uint32_le (logbook + offset + 0); - unsigned int ccrc = checksum_crc32b (header, headersize - unused); + unsigned int ccrc = checksum_crc32 (header, headersize - unused); if (crc != ccrc) { WARNING (abstract->context, "Invalid dive checksum (%08x %08x)", crc, ccrc); status = DC_STATUS_DATAFORMAT; diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index 6ffc7bc..c82db4d 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -352,7 +352,7 @@ suunto_eonsteel_receive_ble(suunto_eonsteel_device_t *device, unsigned char data unsigned int nbytes = transferred - CRC_SIZE; unsigned int crc = array_uint32_le(buffer + nbytes); - unsigned int ccrc = checksum_crc32(buffer, nbytes); + unsigned int ccrc = checksum_crc32r(buffer, nbytes); if (crc != ccrc) { ERROR(device->base.context, "Invalid checksum (expected %08x, received %08x).", ccrc, crc); return DC_STATUS_PROTOCOL; @@ -411,7 +411,7 @@ suunto_eonsteel_send(suunto_eonsteel_device_t *device, } // 4 byte LE checksum - unsigned int crc = checksum_crc32(buf + 2, size + HEADER_SIZE); + unsigned int crc = checksum_crc32r(buf + 2, size + HEADER_SIZE); put_le32(crc, buf + 14 + size); if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) { From 27b471e76bba810c8f8b5102fcaf8e88a53b1d10 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 10 Feb 2023 21:12:05 +0100 Subject: [PATCH 04/25] Document the properties of the CRC functions --- src/checksum.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/checksum.c b/src/checksum.c index b271d04..57d51ae 100644 --- a/src/checksum.c +++ b/src/checksum.c @@ -68,6 +68,11 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char } +/* + * Polynomial: 0x1021 + * RefIn: False + * RefOut: False + */ unsigned short checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout) { @@ -114,6 +119,11 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh } +/* + * Polynomial: 0x1021 + * RefIn: True + * RefOut: True + */ unsigned short checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout) { @@ -160,6 +170,13 @@ checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned s } +/* + * Polynomial: 0x04C11DB7 + * Init: 0xffffffff + * XorOut: 0xffffffff + * RefIn: True + * RefOut: True + */ unsigned int checksum_crc32r (const unsigned char data[], unsigned int size) { @@ -205,6 +222,14 @@ checksum_crc32r (const unsigned char data[], unsigned int size) return crc ^ 0xffffffff; } + +/* + * Polynomial: 0x04C11DB7 + * Init: 0xffffffff + * XorOut: 0xffffffff + * RefIn: False + * RefOut: False + */ unsigned int checksum_crc32 (const unsigned char data[], unsigned int size) { From 554855cc7d015a25a290c21d4a1fb723946004cc Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 2 Mar 2023 18:04:57 +0100 Subject: [PATCH 05/25] Add the 16-bit CRC-ANSI functions Yet another family of 16-bit CRC function. The only difference with the already available CRC-CCITT algorithm is the choice of the polynomial. --- src/checksum.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ src/checksum.h | 6 +++ 2 files changed, 106 insertions(+) diff --git a/src/checksum.c b/src/checksum.c index 57d51ae..eeda912 100644 --- a/src/checksum.c +++ b/src/checksum.c @@ -169,6 +169,106 @@ checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned s return crc ^ xorout; } +/* + * Polynomial: 0x8005 + * RefIn: False + * RefOut: False + */ +unsigned short +checksum_crc16_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout) +{ + static const unsigned short crc_ccitt_table[] = { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 + }; + + unsigned short crc = init; + for (unsigned int i = 0; i < size; ++i) + crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]]; + + return crc ^ xorout; +} + +/* + * Polynomial: 0x8005 + * RefIn: True + * RefOut: True + */ +unsigned short +checksum_crc16r_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout) +{ + static const unsigned short crc_ccitt_table[] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 + }; + + unsigned short crc = init; + for (unsigned int i = 0; i < size; ++i) + crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ data[i]) & 0xff]; + + return crc ^ xorout; +} + /* * Polynomial: 0x04C11DB7 diff --git a/src/checksum.h b/src/checksum.h index 5c91d0a..f9e54bb 100644 --- a/src/checksum.h +++ b/src/checksum.h @@ -44,6 +44,12 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh unsigned short checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); +unsigned short +checksum_crc16_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); + +unsigned short +checksum_crc16r_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout); + unsigned int checksum_crc32r (const unsigned char data[], unsigned int size); From 86fd58c8c6bcc24184150ce78af8b5b0f23d1eba Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 27 Feb 2023 20:14:59 +0100 Subject: [PATCH 06/25] Move the sign extension function to a common place --- src/array.c | 20 ++++++++++++++++++++ src/array.h | 3 +++ src/uwatec_smart_parser.c | 21 +-------------------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/array.c b/src/array.c index 1216d41..29f5faf 100644 --- a/src/array.c +++ b/src/array.c @@ -384,3 +384,23 @@ dec2bcd (unsigned char value) unsigned char lo = value % 10; return (hi << 4) | lo; } + +/* + * When turning a two's-complement number with a certain number + * of bits into one with more bits, the sign bit must be repeated + * in all the extra bits. + */ +unsigned int +signextend (unsigned int value, unsigned int nbits) +{ + if (nbits <= 0 || nbits > 32) + return 0; + + unsigned int signbit = 1U << (nbits - 1); + unsigned int mask = signbit - 1; + + if ((value & signbit) == signbit) + return value | ~mask; + else + return value & mask; +} diff --git a/src/array.h b/src/array.h index ac4c3b1..a6ef21e 100644 --- a/src/array.h +++ b/src/array.h @@ -123,6 +123,9 @@ bcd2dec (unsigned char value); unsigned char dec2bcd (unsigned char value); +unsigned int +signextend (unsigned int value, unsigned int nbits); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/uwatec_smart_parser.c b/src/uwatec_smart_parser.c index 39a927c..bd849a8 100644 --- a/src/uwatec_smart_parser.c +++ b/src/uwatec_smart_parser.c @@ -893,25 +893,6 @@ uwatec_galileo_identify (unsigned char value) } -static unsigned int -uwatec_smart_fixsignbit (unsigned int x, unsigned int n) -{ - if (n <= 0 || n > 32) - return 0; - - unsigned int signbit = (1 << (n - 1)); - unsigned int mask = (signbit - 1); - - // When turning a two's-complement number with a certain number - // of bits into one with more bits, the sign bit must be repeated - // in all the extra bits. - if ((x & signbit) == signbit) - return x | ~mask; - else - return x & mask; -} - - static dc_status_t uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback, void *userdata) { @@ -1018,7 +999,7 @@ uwatec_smart_parse (uwatec_smart_parser_t *parser, dc_sample_callback_t callback } // Fix the sign bit. - signed int svalue = uwatec_smart_fixsignbit (value, nbits); + signed int svalue = signextend (value, nbits); // Parse the value. unsigned int idx = 0; From cf221de9b709355bd1d6af9bd6847583793f57b9 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 19:45:21 +0200 Subject: [PATCH 07/25] Disable the getopt argument permutation on Android On Android operating systems, the getopt() function is posix compliant and thus the option processing stops when the first non-option is found. But the getopt_long() function permutes the argument vector, just like the GNU implementation. Using a leading '+' character in the option string disables the permutation again. --- examples/dctool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/dctool.c b/examples/dctool.c index 9d3f74b..62a15fc 100644 --- a/examples/dctool.c +++ b/examples/dctool.c @@ -50,7 +50,7 @@ #define RESET 1 #endif -#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) +#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) || defined(__ANDROID__) #define NOPERMUTATION "+" #else #define NOPERMUTATION "" From 118f6d79ba2ada3f47950f1c2ef2756b515611b8 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 20:24:29 +0200 Subject: [PATCH 08/25] Add a basic Android build system Add a basic Android.mk for building with the Android NDK. This can serve as a good starting point for developers integrating libdivecomputer into an Android application. Co-authored-by: Sven Knoch --- .github/workflows/build.yml | 17 +++++ Makefile.am | 1 + contrib/android/Android.mk | 139 ++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 contrib/android/Android.mk diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b23c7f2..b1d7b71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -152,3 +152,20 @@ jobs: with: name: ${{ github.job }}-${{ matrix.platform }} path: msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin + + android: + + name: Android + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - run: | + autoreconf --install --force + ./configure --prefix=/usr + - run: $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk + - uses: actions/upload-artifact@v3 + with: + name: ${{ github.job }} + path: contrib/android/libs diff --git a/Makefile.am b/Makefile.am index 9ae4414..617a105 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,5 +16,6 @@ pkgconfig_DATA = libdivecomputer.pc EXTRA_DIST = \ libdivecomputer.pc.in \ + contrib/android/Android.mk \ msvc/libdivecomputer.vcxproj \ msvc/libdivecomputer.vcxproj.filters diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk new file mode 100644 index 0000000..15617d6 --- /dev/null +++ b/contrib/android/Android.mk @@ -0,0 +1,139 @@ +LOCAL_PATH := $(call my-dir)/../.. + +include $(CLEAR_VARS) +LOCAL_MODULE := libdivecomputer +LOCAL_CFLAGS := -DENABLE_LOGGING -DHAVE_PTHREAD_H -DHAVE_STRERROR_R -DHAVE_CLOCK_GETTIME -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DHAVE_TIMEGM -DHAVE_STRUCT_TM_TM_GMTOFF +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_SRC_FILES := \ + src/aes.c \ + src/array.c \ + src/atomics_cobalt.c \ + src/atomics_cobalt_parser.c \ + src/bluetooth.c \ + src/buffer.c \ + src/checksum.c \ + src/citizen_aqualand.c \ + src/citizen_aqualand_parser.c \ + src/cochran_commander.c \ + src/cochran_commander_parser.c \ + src/common.c \ + src/context.c \ + src/cressi_edy.c \ + src/cressi_edy_parser.c \ + src/cressi_goa.c \ + src/cressi_goa_parser.c \ + src/cressi_leonardo.c \ + src/cressi_leonardo_parser.c \ + src/custom.c \ + src/datetime.c \ + src/deepblu_cosmiq.c \ + src/deepblu_cosmiq_parser.c \ + src/deepsix_excursion.c \ + src/deepsix_excursion_parser.c \ + src/descriptor.c \ + src/device.c \ + src/diverite_nitekq.c \ + src/diverite_nitekq_parser.c \ + src/divesystem_idive.c \ + src/divesystem_idive_parser.c \ + src/hw_frog.c \ + src/hw_ostc3.c \ + src/hw_ostc.c \ + src/hw_ostc_parser.c \ + src/ihex.c \ + src/iostream.c \ + src/irda.c \ + src/iterator.c \ + src/liquivision_lynx.c \ + src/liquivision_lynx_parser.c \ + src/mares_common.c \ + src/mares_darwin.c \ + src/mares_darwin_parser.c \ + src/mares_iconhd.c \ + src/mares_iconhd_parser.c \ + src/mares_nemo.c \ + src/mares_nemo_parser.c \ + src/mares_puck.c \ + src/mclean_extreme.c \ + src/mclean_extreme_parser.c \ + src/oceanic_atom2.c \ + src/oceanic_atom2_parser.c \ + src/oceanic_common.c \ + src/oceanic_veo250.c \ + src/oceanic_veo250_parser.c \ + src/oceanic_vtpro.c \ + src/oceanic_vtpro_parser.c \ + src/oceans_s1.c \ + src/oceans_s1_common.c \ + src/oceans_s1_parser.c \ + src/parser.c \ + src/platform.c \ + src/rbstream.c \ + src/reefnet_sensus.c \ + src/reefnet_sensus_parser.c \ + src/reefnet_sensuspro.c \ + src/reefnet_sensuspro_parser.c \ + src/reefnet_sensusultra.c \ + src/reefnet_sensusultra_parser.c \ + src/ringbuffer.c \ + src/seac_screen.c \ + src/seac_screen_parser.c \ + src/serial_posix.c \ + src/shearwater_common.c \ + src/shearwater_petrel.c \ + src/shearwater_predator.c \ + src/shearwater_predator_parser.c \ + src/socket.c \ + src/sporasub_sp2.c \ + src/sporasub_sp2_parser.c \ + src/suunto_common2.c \ + src/suunto_common.c \ + src/suunto_d9.c \ + src/suunto_d9_parser.c \ + src/suunto_eon.c \ + src/suunto_eon_parser.c \ + src/suunto_eonsteel.c \ + src/suunto_eonsteel_parser.c \ + src/suunto_solution.c \ + src/suunto_solution_parser.c \ + src/suunto_vyper2.c \ + src/suunto_vyper.c \ + src/suunto_vyper_parser.c \ + src/tecdiving_divecomputereu.c \ + src/tecdiving_divecomputereu_parser.c \ + src/timer.c \ + src/usb.c \ + src/usbhid.c \ + src/uwatec_aladin.c \ + src/uwatec_memomouse.c \ + src/uwatec_memomouse_parser.c \ + src/uwatec_smart.c \ + src/uwatec_smart_parser.c \ + src/version.c \ + src/zeagle_n2ition3.c +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := dctool +LOCAL_SHARED_LIBRARIES := libdivecomputer +LOCAL_CFLAGS := -DHAVE_UNISTD_H -DHAVE_GETOPT_H -DHAVE_GETOPT_LONG -DHAVE_DECL_OPTRESET=1 +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_SRC_FILES := \ + examples/common.c \ + examples/dctool.c \ + examples/dctool_download.c \ + examples/dctool_dump.c \ + examples/dctool_fwupdate.c \ + examples/dctool_help.c \ + examples/dctool_list.c \ + examples/dctool_parse.c \ + examples/dctool_read.c \ + examples/dctool_scan.c \ + examples/dctool_timesync.c \ + examples/dctool_version.c \ + examples/dctool_write.c \ + examples/output.c \ + examples/output_raw.c \ + examples/output_xml.c \ + examples/utils.c +include $(BUILD_EXECUTABLE) From 083b1eb8dee0cee0f5af25aff9315c3d0bf50561 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 21:43:55 +0200 Subject: [PATCH 09/25] Move the Visual Studio project to the contrib directory With this change all the alternative build systems are now located in the contrib directory. --- .github/workflows/build.yml | 4 +- Makefile.am | 4 +- contrib/msvc/libdivecomputer.vcxproj | 388 ++++++++++++++++++ .../msvc}/libdivecomputer.vcxproj.filters | 0 msvc/libdivecomputer.vcxproj | 388 ------------------ 5 files changed, 392 insertions(+), 392 deletions(-) create mode 100644 contrib/msvc/libdivecomputer.vcxproj rename {msvc => contrib/msvc}/libdivecomputer.vcxproj.filters (100%) delete mode 100644 msvc/libdivecomputer.vcxproj diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1d7b71..3d7587c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -147,11 +147,11 @@ jobs: ./configure --prefix=/usr shell: msys2 {0} - uses: microsoft/setup-msbuild@v1 - - run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} msvc/libdivecomputer.vcxproj + - run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj - uses: actions/upload-artifact@v3 with: name: ${{ github.job }}-${{ matrix.platform }} - path: msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin + path: contrib/msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin android: diff --git a/Makefile.am b/Makefile.am index 617a105..f60d2af 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,5 +17,5 @@ pkgconfig_DATA = libdivecomputer.pc EXTRA_DIST = \ libdivecomputer.pc.in \ contrib/android/Android.mk \ - msvc/libdivecomputer.vcxproj \ - msvc/libdivecomputer.vcxproj.filters + contrib/msvc/libdivecomputer.vcxproj \ + contrib/msvc/libdivecomputer.vcxproj.filters diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj new file mode 100644 index 0000000..dbdb1e1 --- /dev/null +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -0,0 +1,388 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {CEA7215A-D6B5-4840-8086-3C854F371997} + libdivecomputer + Win32Proj + + + + DynamicLibrary + true + $(DefaultPlatformToolset) + Unicode + + + DynamicLibrary + true + $(DefaultPlatformToolset) + Unicode + + + DynamicLibrary + false + $(DefaultPlatformToolset) + Unicode + true + + + DynamicLibrary + false + $(DefaultPlatformToolset) + Unicode + true + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ + $(PlatformTarget)\$(Configuration)\obj\ + + + true + $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ + $(PlatformTarget)\$(Configuration)\obj\ + + + false + $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ + $(PlatformTarget)\$(Configuration)\obj\ + + + false + $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ + $(PlatformTarget)\$(Configuration)\obj\ + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + + Level3 + true + + + ws2_32.lib;%(AdditionalDependencies) + $(OutDir)libdivecomputer.def + true + Windows + + + + + Disabled + ..\..\include;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + + + Level3 + true + + + ws2_32.lib;%(AdditionalDependencies) + $(OutDir)libdivecomputer.def + true + Windows + + + + + MaxSpeed + true + ..\..\include;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + true + + Level3 + true + + + ws2_32.lib;%(AdditionalDependencies) + $(OutDir)libdivecomputer.def + true + Windows + true + true + + + + + MaxSpeed + true + ..\..\include;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + true + + + Level3 + true + + + ws2_32.lib;%(AdditionalDependencies) + $(OutDir)libdivecomputer.def + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" + echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" + $(OutDir)libdivecomputer.def;%(Outputs) + $(OutDir)libdivecomputer.def;%(Outputs) + echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" + echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" + $(OutDir)libdivecomputer.def;%(Outputs) + $(OutDir)libdivecomputer.def;%(Outputs) + + + + + + diff --git a/msvc/libdivecomputer.vcxproj.filters b/contrib/msvc/libdivecomputer.vcxproj.filters similarity index 100% rename from msvc/libdivecomputer.vcxproj.filters rename to contrib/msvc/libdivecomputer.vcxproj.filters diff --git a/msvc/libdivecomputer.vcxproj b/msvc/libdivecomputer.vcxproj deleted file mode 100644 index b3b370f..0000000 --- a/msvc/libdivecomputer.vcxproj +++ /dev/null @@ -1,388 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {CEA7215A-D6B5-4840-8086-3C854F371997} - libdivecomputer - Win32Proj - - - - DynamicLibrary - true - $(DefaultPlatformToolset) - Unicode - - - DynamicLibrary - true - $(DefaultPlatformToolset) - Unicode - - - DynamicLibrary - false - $(DefaultPlatformToolset) - Unicode - true - - - DynamicLibrary - false - $(DefaultPlatformToolset) - Unicode - true - - - - - - - - - - - - - - - - - - - true - $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ - $(PlatformTarget)\$(Configuration)\obj\ - - - true - $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ - $(PlatformTarget)\$(Configuration)\obj\ - - - false - $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ - $(PlatformTarget)\$(Configuration)\obj\ - - - false - $(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\ - $(PlatformTarget)\$(Configuration)\obj\ - - - - Disabled - ..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) - - Level3 - true - - - ws2_32.lib;%(AdditionalDependencies) - $(OutDir)libdivecomputer.def - true - Windows - - - - - Disabled - ..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) - - - Level3 - true - - - ws2_32.lib;%(AdditionalDependencies) - $(OutDir)libdivecomputer.def - true - Windows - - - - - MaxSpeed - true - ..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) - true - - Level3 - true - - - ws2_32.lib;%(AdditionalDependencies) - $(OutDir)libdivecomputer.def - true - Windows - true - true - - - - - MaxSpeed - true - ..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) - true - - - Level3 - true - - - ws2_32.lib;%(AdditionalDependencies) - $(OutDir)libdivecomputer.def - true - Windows - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" - echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" - $(OutDir)libdivecomputer.def;%(Outputs) - $(OutDir)libdivecomputer.def;%(Outputs) - echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" - echo EXPORTS > "$(OutDir)libdivecomputer.def" && type "%(FullPath)" >> "$(OutDir)libdivecomputer.def" - $(OutDir)libdivecomputer.def;%(Outputs) - $(OutDir)libdivecomputer.def;%(Outputs) - - - - - - From 767a2fad9120f0cbbaa0cffae4ade1a9c46125ec Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 22:25:20 +0200 Subject: [PATCH 10/25] Don't generate the Windows version resource The Windows version resource is compiled and can include the (generated) version.h header file for the definition of the version macros. There is no need to have it generated by autotools. Less generated files makes it easier to use other build systems, like Visual Studio. --- configure.ac | 1 - contrib/msvc/libdivecomputer.vcxproj | 12 +++++ src/Makefile.am | 2 +- src/libdivecomputer.rc | 68 ++++++++++++++++++++++++++++ src/libdivecomputer.rc.in | 45 ------------------ 5 files changed, 81 insertions(+), 47 deletions(-) create mode 100644 src/libdivecomputer.rc delete mode 100644 src/libdivecomputer.rc.in diff --git a/configure.ac b/configure.ac index 32496f6..e46f8f1 100644 --- a/configure.ac +++ b/configure.ac @@ -238,7 +238,6 @@ AC_CONFIG_FILES([ include/libdivecomputer/Makefile include/libdivecomputer/version.h src/Makefile - src/libdivecomputer.rc doc/Makefile doc/doxygen.cfg doc/man/Makefile diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index dbdb1e1..10dba0f 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -101,6 +101,9 @@ true Windows + + ..\..\include;%(AdditionalIncludeDirectories) + @@ -118,6 +121,9 @@ true Windows + + ..\..\include;%(AdditionalIncludeDirectories) + @@ -138,6 +144,9 @@ true true + + ..\..\include;%(AdditionalIncludeDirectories) + @@ -159,6 +168,9 @@ true true + + ..\..\include;%(AdditionalIncludeDirectories) + diff --git a/src/Makefile.am b/src/Makefile.am index 72961b8..9234ba7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -103,7 +103,7 @@ libdivecomputer.exp: libdivecomputer.symbols $(AM_V_GEN) sed -e '/^$$/d' $< > $@ .rc.lo: - $(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $< -o $@ + $(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $< -o $@ libdivecomputer.lo: revision.h diff --git a/src/libdivecomputer.rc b/src/libdivecomputer.rc new file mode 100644 index 0000000..1441085 --- /dev/null +++ b/src/libdivecomputer.rc @@ -0,0 +1,68 @@ +/* + * libdivecomputer + * + * Copyright (C) 2010 Jef Driesen + * + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#ifdef HAVE_VERSION_SUFFIX +#include "revision.h" +#endif + +VS_VERSION_INFO VERSIONINFO +FILEVERSION DC_VERSION_MAJOR,DC_VERSION_MINOR,DC_VERSION_MICRO,0 +PRODUCTVERSION DC_VERSION_MAJOR,DC_VERSION_MINOR,DC_VERSION_MICRO,0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef HAVE_VERSION_SUFFIX +FILEFLAGS VS_FF_PRERELEASE +#else +FILEFLAGS 0 +#endif +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE 0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "The libdivecomputer developers" + VALUE "FileDescription", "A library for communication with various dive computers." + VALUE "FileVersion", DC_VERSION + VALUE "InternalName", "libdivecomputer" + VALUE "LegalCopyright", "Copyright © 2010 Jef Driesen" + VALUE "OriginalFilename", "libdivecomputer.dll" + VALUE "ProductName", "libdivecomputer" + VALUE "ProductVersion", DC_VERSION +#ifdef HAVE_VERSION_SUFFIX + VALUE "Comments", DC_VERSION_REVISION +#endif + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 1033, 1200 + END +END diff --git a/src/libdivecomputer.rc.in b/src/libdivecomputer.rc.in deleted file mode 100644 index f98d2ed..0000000 --- a/src/libdivecomputer.rc.in +++ /dev/null @@ -1,45 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#ifdef HAVE_VERSION_SUFFIX -#include "revision.h" -#endif - -VS_VERSION_INFO VERSIONINFO -FILEVERSION @DC_VERSION_MAJOR@,@DC_VERSION_MINOR@,@DC_VERSION_MICRO@,0 -PRODUCTVERSION @DC_VERSION_MAJOR@,@DC_VERSION_MINOR@,@DC_VERSION_MICRO@,0 -FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef HAVE_VERSION_SUFFIX -FILEFLAGS VS_FF_PRERELEASE -#else -FILEFLAGS 0 -#endif -FILEOS VOS__WINDOWS32 -FILETYPE VFT_DLL -FILESUBTYPE 0 -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904B0" - BEGIN - VALUE "CompanyName", "The libdivecomputer developers" - VALUE "FileDescription", "A library for communication with various dive computers." - VALUE "FileVersion", "@VERSION@" - VALUE "InternalName", "libdivecomputer" - VALUE "LegalCopyright", "Copyright 2010 Jef Driesen" - VALUE "OriginalFilename", "libdivecomputer.dll" - VALUE "ProductName", "libdivecomputer" - VALUE "ProductVersion", "@VERSION@" -#ifdef HAVE_VERSION_SUFFIX - VALUE "Comments", DC_VERSION_REVISION -#endif - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 1033, 1200 - END -END From e45c62b0283e19c7aa4149304949a5b04ebdb0a0 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 22:39:08 +0200 Subject: [PATCH 11/25] Include the revision in the Visual Studio and Android builds During troubleshooting it's very convenient to know the exact version used in a bug report. With the git commit SHA1 added to the version string in all builds, that becomes very easy. --- .github/workflows/build.yml | 2 ++ contrib/android/Android.mk | 2 +- contrib/msvc/libdivecomputer.vcxproj | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d7587c..00ff527 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -145,6 +145,7 @@ jobs: - run: | autoreconf --install --force ./configure --prefix=/usr + make -C src revision.h shell: msys2 {0} - uses: microsoft/setup-msbuild@v1 - run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj @@ -164,6 +165,7 @@ jobs: - 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 with: diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 15617d6..61961a0 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH := $(call my-dir)/../.. include $(CLEAR_VARS) LOCAL_MODULE := libdivecomputer -LOCAL_CFLAGS := -DENABLE_LOGGING -DHAVE_PTHREAD_H -DHAVE_STRERROR_R -DHAVE_CLOCK_GETTIME -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DHAVE_TIMEGM -DHAVE_STRUCT_TM_TM_GMTOFF +LOCAL_CFLAGS := -DENABLE_LOGGING -DHAVE_VERSION_SUFFIX -DHAVE_PTHREAD_H -DHAVE_STRERROR_R -DHAVE_CLOCK_GETTIME -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DHAVE_TIMEGM -DHAVE_STRUCT_TM_TM_GMTOFF LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_SRC_FILES := \ src/aes.c \ diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index 10dba0f..406c577 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -90,7 +90,7 @@ Disabled ..\..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) Level3 true @@ -109,7 +109,7 @@ Disabled ..\..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) Level3 @@ -130,7 +130,7 @@ MaxSpeed true ..\..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) true Level3 @@ -153,7 +153,7 @@ MaxSpeed true ..\..\include;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions) true From 43f48af418c8cf38a5ff506612cd0ecf17aeb070 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 23:01:12 +0200 Subject: [PATCH 12/25] Add a README file to the contrib directory --- Makefile.am | 1 + contrib/README | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 contrib/README diff --git a/Makefile.am b/Makefile.am index f60d2af..c69320d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,6 +16,7 @@ pkgconfig_DATA = libdivecomputer.pc EXTRA_DIST = \ libdivecomputer.pc.in \ + contrib/README \ contrib/android/Android.mk \ contrib/msvc/libdivecomputer.vcxproj \ contrib/msvc/libdivecomputer.vcxproj.filters diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..cb0b77c --- /dev/null +++ b/contrib/README @@ -0,0 +1,56 @@ +Alternative build systems +========================= + +The autotools based build system is the official build system for the +libdivecomputer project. But for convenience, a few alternative build systems +are available as well. Unfortunately, these builds systems require a few extra +steps to generate some header files. + +If you have access to a UNIX build system (for example a Linux virtual machine, +MinGW, Cygwin or the Windows Subsystem for Linux), you can use the autotools +build system to generate those files: + + $ autoreconf --install --force + $ ./configure + $ make -C src revision.h + +Alternative, you can generate those files manually. First, create the version.h +file from the version.h.in template: + + $ cp include/libdivecomputer/version.h.in include/libdivecomputer/version.h + +and replace all the @DC_VERSION@ placeholders with the values defined in the +configure.ac file. + +Next, generate the revision.h file: + + $ echo "#define DC_VERSION_REVISION \"$(git rev-parse --verify HEAD)\"" > src/revision.h + +The alternative build systems are ready to use now. + +Visual Studio +------------- + +The Visual Studio project file can be opened in the IDE, or build directly from +the command-line: + + msbuild -m -p:Platform=x86|x64 -p:Configuration=Debug|Release contrib/msvc/libdivecomputer.vcxproj + +Android NDK +----------- + + $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk + +Linux udev rules +================ + +For dive computers using USB or USB HID communication, regular users typically +don't have the necessary permissions to access the device nodes. This can be +fixed with some udev rules. + +Install the udev rules, and reload them: + + $ sudo cp contrib/udev/libdivecomputer.rules /etc/udev/rules.d/ + $ sudo udevadm control --reload + +Note: the provided udev rules assume the user is in the plugdev group. From bec4a747ff0492a87d30bafcbe899e97f6d83ece Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 6 Apr 2023 23:12:01 +0200 Subject: [PATCH 13/25] Add the udev rules to the distribution tarball --- Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index c69320d..3798611 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,4 +19,5 @@ EXTRA_DIST = \ contrib/README \ contrib/android/Android.mk \ contrib/msvc/libdivecomputer.vcxproj \ - contrib/msvc/libdivecomputer.vcxproj.filters + contrib/msvc/libdivecomputer.vcxproj.filters \ + contrib/udev/libdivecomputer.rules From 629d5673812407f1d3454362754cf1aa54dd10fb Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 30 Mar 2023 21:26:48 +0200 Subject: [PATCH 14/25] Use the correct function to free resources Currently this doesn't make any difference because the dc_device_allocate() function simply calls free(), but this may change in the future. --- src/suunto_eonsteel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index c82db4d..fd4ecc7 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -806,7 +806,7 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea return DC_STATUS_SUCCESS; error_free: - free(eon); + dc_device_deallocate ((dc_device_t *) eon); return status; } From 9c38ae3e01ca3f7f73c4474df4a4dfe863ac22bb Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 30 Mar 2023 21:35:28 +0200 Subject: [PATCH 15/25] Remove the local endianess functions Replace the local functions for writing 16 and 32 bit little endian values with the corresponding common functions. --- src/suunto_eonsteel.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index fd4ecc7..2f1335f 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -127,20 +127,6 @@ static struct directory_entry *alloc_dirent(int type, int len, const char *name) return res; } -static void put_le16(unsigned short val, unsigned char *p) -{ - p[0] = val; - p[1] = val >> 8; -} - -static void put_le32(unsigned int val, unsigned char *p) -{ - p[0] = val; - p[1] = val >> 8; - p[2] = val >> 16; - p[3] = val >> 24; -} - static dc_status_t suunto_eonsteel_hdlc_write (suunto_eonsteel_device_t *device, const unsigned char data[], size_t size, size_t *actual) { @@ -394,16 +380,16 @@ suunto_eonsteel_send(suunto_eonsteel_device_t *device, buf[1] = size + HEADER_SIZE; // 2-byte LE command word - put_le16(cmd, buf + 2); + array_uint16_le_set(buf + 2, cmd); // 4-byte LE magic value (starts at 1) - put_le32(device->magic, buf + 4); + array_uint32_le_set(buf + 4, device->magic); // 2-byte LE sequence number; - put_le16(device->seq, buf + 8); + array_uint16_le_set(buf + 8, device->seq); // 4-byte LE length - put_le32(size, buf + 10); + array_uint32_le_set(buf + 10, size); // .. followed by actual data if (size) { @@ -412,7 +398,7 @@ suunto_eonsteel_send(suunto_eonsteel_device_t *device, // 4 byte LE checksum unsigned int crc = checksum_crc32r(buf + 2, size + HEADER_SIZE); - put_le32(crc, buf + 14 + size); + array_uint32_le_set(buf + 14 + size, crc); if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) { rc = suunto_eonsteel_hdlc_write(device, buf + 2, size + HEADER_SIZE + CRC_SIZE, NULL); @@ -591,8 +577,8 @@ read_file(suunto_eonsteel_device_t *eon, const char *filename, dc_buffer_t *buf) ask = size; if (ask > 1024) ask = 1024; - put_le32(1234, cmdbuf+0); // Not file offset, after all - put_le32(ask, cmdbuf+4); // Size of read + array_uint32_le_set(cmdbuf + 0, 1234); // Not file offset, after all + array_uint32_le_set(cmdbuf + 4, ask); // Size of read rc = suunto_eonsteel_transfer(eon, CMD_FILE_READ, cmdbuf, 8, result, sizeof(result), &n); if (rc != DC_STATUS_SUCCESS) { @@ -705,7 +691,7 @@ get_file_list(suunto_eonsteel_device_t *eon, struct directory_entry **res) unsigned int n = 0; unsigned int cmdlen; - put_le32(0, cmd); + array_uint32_le_set(cmd, 0); memcpy(cmd + 4, dive_directory, sizeof(dive_directory)); cmdlen = 4 + sizeof(dive_directory); rc = suunto_eonsteel_transfer(eon, CMD_DIR_OPEN, @@ -890,7 +876,7 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac break; } - put_le32(time, buf); + array_uint32_le_set(buf, time); if (memcmp (buf, eon->fingerprint, sizeof (eon->fingerprint)) == 0) { skip = 1; From cee9a2e926adb12f422138f46c2213c1c5576e29 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 20 Mar 2023 21:29:27 +0100 Subject: [PATCH 16/25] Add a generic HDLC I/O implementation The new HDLC I/O provides a generic I/O stream for reading and writing HDLC encoded frames on top of another base I/O stream. --- contrib/android/Android.mk | 1 + contrib/msvc/libdivecomputer.vcxproj | 2 + src/Makefile.am | 1 + src/hdlc.c | 406 +++++++++++++++++++++++++++ src/hdlc.h | 50 ++++ 5 files changed, 460 insertions(+) create mode 100644 src/hdlc.c create mode 100644 src/hdlc.h diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 61961a0..d2d8cd1 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -36,6 +36,7 @@ LOCAL_SRC_FILES := \ src/diverite_nitekq_parser.c \ src/divesystem_idive.c \ src/divesystem_idive_parser.c \ + src/hdlc.c \ src/hw_frog.c \ src/hw_ostc3.c \ src/hw_ostc.c \ diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index 406c577..6497705 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -204,6 +204,7 @@ + @@ -331,6 +332,7 @@ + diff --git a/src/Makefile.am b/src/Makefile.am index 9234ba7..1b37d9c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -80,6 +80,7 @@ libdivecomputer_la_SOURCES = \ deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \ oceans_s1_common.h oceans_s1_common.c \ oceans_s1.h oceans_s1.c oceans_s1_parser.c \ + hdlc.h hdlc.c \ socket.h socket.c \ irda.c \ usb.c \ diff --git a/src/hdlc.c b/src/hdlc.c new file mode 100644 index 0000000..40e1ae7 --- /dev/null +++ b/src/hdlc.c @@ -0,0 +1,406 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jef Driesen + * + * 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 // malloc, free + +#include "hdlc.h" + +#include "iostream-private.h" +#include "common-private.h" +#include "context-private.h" + +#define END 0x7E +#define ESC 0x7D +#define ESC_BIT 0x20 + +static dc_status_t dc_hdlc_set_timeout (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_hdlc_set_break (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_hdlc_set_dtr (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_hdlc_set_rts (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_hdlc_get_lines (dc_iostream_t *abstract, unsigned int *value); +static dc_status_t dc_hdlc_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); +static dc_status_t dc_hdlc_poll (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_hdlc_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual); +static dc_status_t dc_hdlc_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual); +static dc_status_t dc_hdlc_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size); +static dc_status_t dc_hdlc_flush (dc_iostream_t *abstract); +static dc_status_t dc_hdlc_purge (dc_iostream_t *abstract, dc_direction_t direction); +static dc_status_t dc_hdlc_sleep (dc_iostream_t *abstract, unsigned int milliseconds); +static dc_status_t dc_hdlc_close (dc_iostream_t *abstract); + +typedef struct dc_hdlc_t { + /* Base class. */ + dc_iostream_t base; + /* Internal state. */ + dc_context_t *context; + dc_iostream_t *iostream; + unsigned char *rbuf; + unsigned char *wbuf; + size_t rbuf_size; + size_t rbuf_offset; + size_t rbuf_available; + size_t wbuf_size; + size_t wbuf_offset; +} dc_hdlc_t; + +static const dc_iostream_vtable_t dc_hdlc_vtable = { + sizeof(dc_hdlc_t), + dc_hdlc_set_timeout, /* set_timeout */ + dc_hdlc_set_break, /* set_break */ + dc_hdlc_set_dtr, /* set_dtr */ + dc_hdlc_set_rts, /* set_rts */ + dc_hdlc_get_lines, /* get_lines */ + NULL, /* get_available */ + dc_hdlc_configure, /* configure */ + dc_hdlc_poll, /* poll */ + dc_hdlc_read, /* read */ + dc_hdlc_write, /* write */ + dc_hdlc_ioctl, /* ioctl */ + dc_hdlc_flush, /* flush */ + dc_hdlc_purge, /* purge */ + dc_hdlc_sleep, /* sleep */ + dc_hdlc_close, /* close */ +}; + +dc_status_t +dc_hdlc_open (dc_iostream_t **out, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_hdlc_t *hdlc = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + if (base == NULL || isize == 0 || osize == 0) + return DC_STATUS_INVALIDARGS; + + dc_transport_t transport = dc_iostream_get_transport (base); + + // Allocate memory. + hdlc = (dc_hdlc_t *) dc_iostream_allocate (NULL, &dc_hdlc_vtable, transport); + if (hdlc == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + // Allocate the read buffer. + hdlc->rbuf = malloc (isize); + if (hdlc->rbuf == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_free; + } + + // Allocate the write buffer. + hdlc->wbuf = malloc (osize); + if (hdlc->wbuf == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_free_rbuf; + } + + hdlc->context = context; + hdlc->iostream = base; + hdlc->rbuf_size = isize; + hdlc->rbuf_offset = 0; + hdlc->rbuf_available = 0; + hdlc->wbuf_size = osize; + hdlc->wbuf_offset = 0; + + *out = (dc_iostream_t *) hdlc; + + return DC_STATUS_SUCCESS; + +error_free_rbuf: + free (hdlc->rbuf); +error_free: + dc_iostream_deallocate ((dc_iostream_t *) hdlc); +error_exit: + return status; +} + +static dc_status_t +dc_hdlc_set_timeout (dc_iostream_t *abstract, int timeout) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_set_timeout (hdlc->iostream, timeout); +} + +static dc_status_t +dc_hdlc_set_break (dc_iostream_t *abstract, unsigned int value) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_set_break (hdlc->iostream, value); +} + +static dc_status_t +dc_hdlc_set_dtr (dc_iostream_t *abstract, unsigned int value) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_set_dtr (hdlc->iostream, value); +} + +static dc_status_t +dc_hdlc_set_rts (dc_iostream_t *abstract, unsigned int value) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_set_rts (hdlc->iostream, value); +} + +static dc_status_t +dc_hdlc_get_lines (dc_iostream_t *abstract, unsigned int *value) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_get_lines (hdlc->iostream, value); +} + +static dc_status_t +dc_hdlc_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_configure (hdlc->iostream, baudrate, databits, parity, stopbits, flowcontrol); +} + +static dc_status_t +dc_hdlc_poll (dc_iostream_t *abstract, int timeout) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + if (hdlc->rbuf_available) { + return DC_STATUS_SUCCESS; + } + + return dc_iostream_poll (hdlc->iostream, timeout); +} + +static dc_status_t +dc_hdlc_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + size_t nbytes = 0; + + unsigned int initialized = 0; + unsigned int escaped = 0; + + while (1) { + if (hdlc->rbuf_available == 0) { + // Read a packet into the cache. + size_t len = 0; + status = dc_iostream_read (hdlc->iostream, hdlc->rbuf, hdlc->rbuf_size, &len); + if (status != DC_STATUS_SUCCESS) { + goto out; + } + + hdlc->rbuf_available = len; + hdlc->rbuf_offset = 0; + } + + while (hdlc->rbuf_available) { + unsigned char c = hdlc->rbuf[hdlc->rbuf_offset]; + hdlc->rbuf_offset++; + hdlc->rbuf_available--; + + if (c == END) { + if (escaped) { + ERROR (hdlc->context, "HDLC frame escaped the special character %02x.", c); + status = DC_STATUS_IO; + goto out; + } + + if (initialized) { + goto out; + } + + initialized = 1; + continue; + } + + if (!initialized) { + continue; + } + + if (c == ESC) { + if (escaped) { + ERROR (hdlc->context, "HDLC frame escaped the special character %02x.", c); + status = DC_STATUS_IO; + goto out; + } + escaped = 1; + continue; + } + + if (escaped) { + c ^= ESC_BIT; + escaped = 0; + } + + if (nbytes < size) + ((unsigned char *)data)[nbytes] = c; + nbytes++; + } + } + +out: + if (nbytes > size) { + ERROR (hdlc->context, "HDLC frame is too large (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", nbytes, size); + dc_status_set_error (&status, DC_STATUS_IO); + nbytes = size; + } + + if (actual) + *actual = nbytes; + + return status; +} + +static dc_status_t +dc_hdlc_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + size_t nbytes = 0; + + // Clear the buffer. + hdlc->wbuf_offset = 0; + + // Start of the packet. + hdlc->wbuf[hdlc->wbuf_offset++] = END; + + // Flush the buffer if necessary. + if (hdlc->wbuf_offset >= hdlc->wbuf_size) { + status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL); + if (status != DC_STATUS_SUCCESS) { + goto out; + } + + hdlc->wbuf_offset = 0; + } + + while (nbytes < size) { + unsigned char c = ((const unsigned char *) data)[nbytes]; + + if (c == END || c == ESC) { + // Append the escape character. + hdlc->wbuf[hdlc->wbuf_offset++] = ESC; + + // Flush the buffer if necessary. + if (hdlc->wbuf_offset >= hdlc->wbuf_size) { + status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL); + if (status != DC_STATUS_SUCCESS) { + goto out; + } + + hdlc->wbuf_offset = 0; + } + + // Escape the character. + c ^= ESC_BIT; + } + + // Append the character. + hdlc->wbuf[hdlc->wbuf_offset++] = c; + + // Flush the buffer if necessary. + if (hdlc->wbuf_offset >= hdlc->wbuf_size) { + status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL); + if (status != DC_STATUS_SUCCESS) { + goto out; + } + + hdlc->wbuf_offset = 0; + } + + nbytes++; + } + + // End of the packet. + hdlc->wbuf[hdlc->wbuf_offset++] = END; + + // Flush the buffer. + status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL); + if (status != DC_STATUS_SUCCESS) { + goto out; + } + + hdlc->wbuf_offset = 0; + +out: + if (actual) + *actual = nbytes; + + return status; +} + +static dc_status_t +dc_hdlc_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_ioctl (hdlc->iostream, request, data, size); +} + +static dc_status_t +dc_hdlc_flush (dc_iostream_t *abstract) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_flush (hdlc->iostream); +} + +static dc_status_t +dc_hdlc_purge (dc_iostream_t *abstract, dc_direction_t direction) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + if (direction & DC_DIRECTION_INPUT) { + hdlc->rbuf_available = 0; + hdlc->rbuf_offset = 0; + } + + return dc_iostream_purge (hdlc->iostream, direction); +} + +static dc_status_t +dc_hdlc_sleep (dc_iostream_t *abstract, unsigned int milliseconds) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + return dc_iostream_sleep (hdlc->iostream, milliseconds); +} + +static dc_status_t +dc_hdlc_close (dc_iostream_t *abstract) +{ + dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract; + + free (hdlc->wbuf); + free (hdlc->rbuf); + + return DC_STATUS_SUCCESS; +} diff --git a/src/hdlc.h b/src/hdlc.h new file mode 100644 index 0000000..aedd9b5 --- /dev/null +++ b/src/hdlc.h @@ -0,0 +1,50 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jef Driesen + * + * 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 DC_HDLC_H +#define DC_HDLC_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Create a HDLC I/O stream layered on top of another base I/O stream. + * + * @param[out] iostream A location to store the HDLC I/O stream. + * @param[in] context A valid context. + * @param[in] base A valid I/O stream. + * @param[in] isize The input packet size in bytes. + * @param[in] osize The output packet size in bytes. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_hdlc_open (dc_iostream_t **iostream, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_HDLC_H */ From b0e77fd05f1f63d74f2c742d8ac2766977cd7aaf Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 31 Mar 2023 19:44:44 +0200 Subject: [PATCH 17/25] Integrate the HDLC stream in the eonsteel backend --- src/suunto_eonsteel.c | 181 ++++++++---------------------------------- 1 file changed, 34 insertions(+), 147 deletions(-) diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c index 2f1335f..a18d27c 100644 --- a/src/suunto_eonsteel.c +++ b/src/suunto_eonsteel.c @@ -29,6 +29,7 @@ #include "array.h" #include "platform.h" #include "checksum.h" +#include "hdlc.h" #define EONSTEEL 0 #define EONCORE 1 @@ -80,14 +81,10 @@ struct directory_entry { #define MAXDATA_SIZE 2048 #define CRC_SIZE 4 -// HDLC special characters -#define END 0x7E -#define ESC 0x7D -#define ESC_BIT 0x20 - static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); static dc_status_t suunto_eonsteel_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime); +static dc_status_t suunto_eonsteel_device_close (dc_device_t *abstract); static const dc_device_vtable_t suunto_eonsteel_device_vtable = { sizeof(suunto_eonsteel_device_t), @@ -98,7 +95,7 @@ static const dc_device_vtable_t suunto_eonsteel_device_vtable = { NULL, /* dump */ suunto_eonsteel_device_foreach, /* foreach */ suunto_eonsteel_device_timesync, /* timesync */ - NULL /* close */ + suunto_eonsteel_device_close /* close */ }; static const char dive_directory[] = "0:/dives"; @@ -127,142 +124,6 @@ static struct directory_entry *alloc_dirent(int type, int len, const char *name) return res; } -static dc_status_t -suunto_eonsteel_hdlc_write (suunto_eonsteel_device_t *device, const unsigned char data[], size_t size, size_t *actual) -{ - dc_status_t status = DC_STATUS_SUCCESS; - unsigned char buffer[20]; - size_t nbytes = 0; - - // Start of the packet. - buffer[nbytes++] = END; - - for (size_t i = 0; i < size; ++i) { - unsigned char c = data[i]; - - if (c == END || c == ESC) { - // Append the escape character. - buffer[nbytes++] = ESC; - - // Flush the buffer if necessary. - if (nbytes >= sizeof(buffer)) { - status = dc_iostream_write(device->iostream, buffer, nbytes, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR(device->base.context, "Failed to send the packet."); - return status; - } - - nbytes = 0; - } - - // Escape the character. - c ^= ESC_BIT; - } - - // Append the character. - buffer[nbytes++] = c; - - // Flush the buffer if necessary. - if (nbytes >= sizeof(buffer)) { - status = dc_iostream_write(device->iostream, buffer, nbytes, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR(device->base.context, "Failed to send the packet."); - return status; - } - - nbytes = 0; - } - } - - // End of the packet. - buffer[nbytes++] = END; - - // Flush the buffer. - status = dc_iostream_write(device->iostream, buffer, nbytes, NULL); - if (status != DC_STATUS_SUCCESS) { - ERROR(device->base.context, "Failed to send the packet."); - return status; - } - - if (actual) - *actual = size; - - return status; - -} - -static dc_status_t -suunto_eonsteel_hdlc_read (suunto_eonsteel_device_t *device, unsigned char data[], size_t size, size_t *actual) -{ - dc_status_t status = DC_STATUS_SUCCESS; - unsigned char buffer[20]; - unsigned int initialized = 0; - unsigned int escaped = 0; - size_t nbytes = 0; - - while (1) { - // Read a single data packet. - size_t transferred = 0; - status = dc_iostream_read(device->iostream, buffer, sizeof(buffer), &transferred); - if (status != DC_STATUS_SUCCESS) { - ERROR(device->base.context, "Failed to receive the packet."); - return status; - } - - for (size_t i = 0; i < transferred; ++i) { - unsigned char c = buffer[i]; - - if (c == END) { - if (escaped) { - ERROR (device->base.context, "HDLC frame escaped the special character %02x.", c); - return DC_STATUS_PROTOCOL; - } - - if (initialized) { - goto done; - } - - initialized = 1; - continue; - } - - if (!initialized) { - continue; - } - - if (c == ESC) { - if (escaped) { - ERROR (device->base.context, "HDLC frame escaped the special character %02x.", c); - return DC_STATUS_PROTOCOL; - } - escaped = 1; - continue; - } - - if (escaped) { - c ^= ESC_BIT; - escaped = 0; - } - - if (nbytes < size) - data[nbytes] = c; - nbytes++; - - } - } - -done: - if (nbytes > size) { - ERROR(device->base.context, "Insufficient buffer space available."); - return DC_STATUS_PROTOCOL; - } - - if (actual) - *actual = nbytes; - - return status; -} - /* * Get a single 64-byte packet from the dive computer. This handles packet * logging and any obvious packet-level errors, and returns the payload of @@ -324,7 +185,7 @@ suunto_eonsteel_receive_ble(suunto_eonsteel_device_t *device, unsigned char data unsigned char buffer[HEADER_SIZE + MAXDATA_SIZE + CRC_SIZE]; size_t transferred = 0; - rc = suunto_eonsteel_hdlc_read(device, buffer, sizeof(buffer), &transferred); + rc = dc_iostream_read(device->iostream, buffer, sizeof(buffer), &transferred); if (rc != DC_STATUS_SUCCESS) { ERROR(device->base.context, "Failed to receive the packet."); return rc; @@ -401,7 +262,7 @@ suunto_eonsteel_send(suunto_eonsteel_device_t *device, array_uint32_le_set(buf + 14 + size, crc); if (dc_iostream_get_transport(device->iostream) == DC_TRANSPORT_BLE) { - rc = suunto_eonsteel_hdlc_write(device, buf + 2, size + HEADER_SIZE + CRC_SIZE, NULL); + rc = dc_iostream_write(device->iostream, buf + 2, size + HEADER_SIZE + CRC_SIZE, NULL); } else { rc = dc_iostream_write(device->iostream, buf, sizeof(buf) - CRC_SIZE, NULL); } @@ -757,6 +618,7 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea { dc_status_t status = DC_STATUS_SUCCESS; suunto_eonsteel_device_t *eon = NULL; + dc_transport_t transport = dc_iostream_get_transport (iostream); if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -766,17 +628,26 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea return DC_STATUS_NOMEMORY; // Set up the magic handshake fields - eon->iostream = iostream; eon->model = model; eon->magic = INIT_MAGIC; eon->seq = INIT_SEQ; memset (eon->version, 0, sizeof (eon->version)); memset (eon->fingerprint, 0, sizeof (eon->fingerprint)); + if (transport == DC_TRANSPORT_BLE) { + status = dc_hdlc_open (&eon->iostream, context, iostream, 20, 20); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the HDLC stream."); + goto error_free; + } + } else { + eon->iostream = iostream; + } + status = dc_iostream_set_timeout(eon->iostream, 5000); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the timeout."); - goto error_free; + goto error_free_iostream; } const unsigned char init[] = {0x02, 0x00, 0x2a, 0x00}; @@ -784,18 +655,34 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostrea init, sizeof(init), eon->version, sizeof(eon->version), NULL); if (status != DC_STATUS_SUCCESS) { ERROR(context, "unable to initialize device"); - goto error_free; + goto error_free_iostream; } *out = (dc_device_t *) eon; return DC_STATUS_SUCCESS; +error_free_iostream: + if (transport == DC_TRANSPORT_BLE) { + dc_iostream_close (eon->iostream); + } error_free: dc_device_deallocate ((dc_device_t *) eon); return status; } +static dc_status_t +suunto_eonsteel_device_close (dc_device_t *abstract) +{ + suunto_eonsteel_device_t *device = (suunto_eonsteel_device_t *) abstract; + + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + return dc_iostream_close (device->iostream); + } + + return DC_STATUS_SUCCESS; +} + static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) { From d4472b758f2f698c23b52efb2193c59467ed69c9 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 22 Feb 2023 00:11:14 +0100 Subject: [PATCH 18/25] Add support for the Divesoft Freedom and Liberty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The latest versions of the Divesoft Freedom (HW 4.x) and Liberty (HW 2.x) dive computers support BLE communication. Previous generations did support only a mass storage mode, where the dives are available as DLF files. The BLE communication protocol uses HDLC framing for the data packets. The dives downloaded over BLE have the same data format as the DLF files. Co-authored-by: Jan Matoušek Tested-by: Jakub Hečko --- contrib/android/Android.mk | 2 + contrib/msvc/libdivecomputer.vcxproj | 3 + examples/common.c | 1 + include/libdivecomputer/common.h | 2 + src/Makefile.am | 1 + src/descriptor.c | 18 + src/device.c | 4 + src/divesoft_freedom.c | 595 ++++++++++++++ src/divesoft_freedom.h | 43 ++ src/divesoft_freedom_parser.c | 1070 ++++++++++++++++++++++++++ src/parser.c | 4 + 11 files changed, 1743 insertions(+) create mode 100644 src/divesoft_freedom.c create mode 100644 src/divesoft_freedom.h create mode 100644 src/divesoft_freedom_parser.c diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index d2d8cd1..6c61f26 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -34,6 +34,8 @@ LOCAL_SRC_FILES := \ src/device.c \ src/diverite_nitekq.c \ src/diverite_nitekq_parser.c \ + src/divesoft_freedom.c \ + src/divesoft_freedom_parser.c \ src/divesystem_idive.c \ src/divesystem_idive_parser.c \ src/hdlc.c \ diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index 6497705..8b3ceab 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -202,6 +202,8 @@ + + @@ -331,6 +333,7 @@ + diff --git a/examples/common.c b/examples/common.c index 6c9a957..bccb8bd 100644 --- a/examples/common.c +++ b/examples/common.c @@ -98,6 +98,7 @@ static const backend_table_t g_backends[] = { {"screen", DC_FAMILY_SEAC_SCREEN, 0}, {"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0}, {"s1", DC_FAMILY_OCEANS_S1, 0}, + {"freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19}, }; static const transport_table_t g_transports[] = { diff --git a/include/libdivecomputer/common.h b/include/libdivecomputer/common.h index c05bee1..6bf3556 100644 --- a/include/libdivecomputer/common.h +++ b/include/libdivecomputer/common.h @@ -118,6 +118,8 @@ typedef enum dc_family_t { DC_FAMILY_DEEPBLU_COSMIQ = (21 << 16), /* Oceans S1 */ DC_FAMILY_OCEANS_S1 = (22 << 16), + /* Divesoft Freedom */ + DC_FAMILY_DIVESOFT_FREEDOM = (23 << 16), } dc_family_t; #ifdef __cplusplus diff --git a/src/Makefile.am b/src/Makefile.am index 1b37d9c..ef44da3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -80,6 +80,7 @@ libdivecomputer_la_SOURCES = \ deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \ oceans_s1_common.h oceans_s1_common.c \ oceans_s1.h oceans_s1.c oceans_s1_parser.c \ + divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \ hdlc.h hdlc.c \ socket.h socket.c \ irda.c \ diff --git a/src/descriptor.c b/src/descriptor.c index e41b016..9858da6 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -65,6 +65,7 @@ static int dc_filter_atomic (dc_transport_t transport, const void *userdata, voi static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_oceans (dc_transport_t transport, const void *userdata, void *params); +static int dc_filter_divesoft (dc_transport_t transport, const void *userdata, void *params); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); @@ -451,6 +452,9 @@ static const dc_descriptor_t g_descriptors[] = { {"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu}, /* Oceans S1 */ {"Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans}, + /* Divesoft Freedom */ + {"Divesoft", "Freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19, DC_TRANSPORT_BLE, dc_filter_divesoft}, + {"Divesoft", "Liberty", DC_FAMILY_DIVESOFT_FREEDOM, 10, DC_TRANSPORT_BLE, dc_filter_divesoft}, }; static int @@ -798,6 +802,20 @@ static int dc_filter_oceans (dc_transport_t transport, const void *userdata, voi return 1; } +static int dc_filter_divesoft (dc_transport_t transport, const void *userdata, void *params) +{ + static const char * const bluetooth[] = { + "Freedom", + "Liberty", + }; + + if (transport == DC_TRANSPORT_BLE) { + return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix); + } + + return 1; +} + dc_status_t dc_descriptor_iterator (dc_iterator_t **out) { diff --git a/src/device.c b/src/device.c index e930ff1..6979f22 100644 --- a/src/device.c +++ b/src/device.c @@ -64,6 +64,7 @@ #include "seac_screen.h" #include "deepblu_cosmiq.h" #include "oceans_s1.h" +#include "divesoft_freedom.h" #include "device-private.h" #include "context-private.h" @@ -239,6 +240,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr case DC_FAMILY_OCEANS_S1: rc = oceans_s1_device_open (&device, context, iostream); break; + case DC_FAMILY_DIVESOFT_FREEDOM: + rc = divesoft_freedom_device_open (&device, context, iostream); + break; default: return DC_STATUS_INVALIDARGS; } diff --git a/src/divesoft_freedom.c b/src/divesoft_freedom.c new file mode 100644 index 0000000..ddf9162 --- /dev/null +++ b/src/divesoft_freedom.c @@ -0,0 +1,595 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jan Matoušek, Jef Driesen + * + * 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 +#include +#include + +#include "divesoft_freedom.h" +#include "context-private.h" +#include "device-private.h" +#include "platform.h" +#include "checksum.h" +#include "array.h" +#include "hdlc.h" + +#define MAXDATA 256 + +#define HEADER_SIGNATURE_V1 0x45766944 // "DivE" +#define HEADER_SIGNATURE_V2 0x45566944 // "DiVE" + +#define HEADER_SIZE_V1 32 +#define HEADER_SIZE_V2 64 + +#define RECORD_SIZE 16 +#define FINGERPRINT_SIZE 20 + +#define INVALID 0xFFFFFFFF +#define COMPRESSION 1 +#define DIRECTION 1 +#define NRECORDS 100 + +#define DEVICE_CCR_CU 1 // Liberty HW rev. 1.X +#define DEVICE_FREEDOM 2 // Freedom HW rev. 2.X +#define DEVICE_FREEDOM3 5 // Freedom HW rev. 3.X +#define DEVICE_CCR_CU15 10 // Liberty HW rev. 2.X, Bluetooth enabled +#define DEVICE_FREEDOM4 19 // Freedom HW rev. 4.X, Bluetooth enabled + +typedef enum message_t { + MSG_ECHO = 0, + MSG_RESULT = 1, + MSG_CONNECT = 2, + MSG_CONNECTED = 3, + MSG_VERSION = 4, + MSG_VERSION_RSP = 5, + MSG_DIVE_DATA = 64, + MSG_DIVE_DATA_RSP = 65, + MSG_DIVE_LIST = 66, + MSG_DIVE_LIST_V1 = 67, + MSG_DIVE_LIST_V2 = 71, +} message_t; + +typedef struct divesoft_freedom_device_t { + dc_device_t base; + dc_iostream_t *iostream; + unsigned char fingerprint[FINGERPRINT_SIZE]; + unsigned int seqnum; +} divesoft_freedom_device_t; + +static dc_status_t divesoft_freedom_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); +static dc_status_t divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t divesoft_freedom_device_close (dc_device_t *device); + +static const dc_device_vtable_t divesoft_freedom_device_vtable = { + sizeof(divesoft_freedom_device_t), + DC_FAMILY_DIVESOFT_FREEDOM, + divesoft_freedom_device_set_fingerprint, /* set_fingerprint */ + NULL, /* read */ + NULL, /* write */ + NULL, /* dump */ + divesoft_freedom_device_foreach, /* foreach */ + NULL, /* timesync */ + divesoft_freedom_device_close, /* close */ +}; + +static dc_status_t +divesoft_freedom_send (divesoft_freedom_device_t *device, message_t message, const unsigned char data[], size_t size) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + size_t nbytes = 0, count = 0; + while (1) { + size_t len = size - nbytes; + if (len > MAXDATA) + len = MAXDATA; + + unsigned int islast = nbytes + len == size; + + unsigned char packet[6 + MAXDATA + 2] = {0}; + packet[0] = ((count & 0x0F) << 4) | (device->seqnum & 0x0F); + packet[1] = 0x80 | (islast << 6); + array_uint16_le_set (packet + 2, message); + array_uint16_le_set (packet + 4, len); + if (len) { + memcpy (packet + 6, data + nbytes, len); + } + unsigned short crc = checksum_crc16r_ccitt (packet, len + 6, 0xFFFF, 0xFFFF); + array_uint16_le_set (packet + 6 + len, crc); + + HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "cmd", packet, 6 + len + 2); + + status = dc_iostream_write (device->iostream, packet, 6 + len + 2, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the packet."); + return status; + } + + nbytes += len; + count++; + + if (islast) + break; + } + + return status; +} + +static dc_status_t +divesoft_freedom_recv (divesoft_freedom_device_t *device, dc_event_progress_t *progress, message_t *message, dc_buffer_t *buffer) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned int msg = INVALID; + + unsigned int count = 0; + while (1) { + size_t len = 0; + unsigned char packet[6 + MAXDATA + 2] = {0}; + status = dc_iostream_read (device->iostream, packet, sizeof(packet), &len); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet."); + return status; + } + + HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "rcv", packet, len); + + if (len < 8) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", len); + return DC_STATUS_PROTOCOL; + } + + unsigned int seqnum = packet[0]; + unsigned int flags = packet[1]; + unsigned int type = array_uint16_le (packet + 2); + unsigned int length = array_uint16_le (packet + 4); + + unsigned int expected = ((count & 0x0F) << 4) | (device->seqnum & 0x0F); + if (seqnum != expected) { + ERROR (abstract->context, "Unexpected packet sequence number (%u %u).", seqnum, expected); + return DC_STATUS_PROTOCOL; + } + + if ((flags & ~0x40) != 0) { + ERROR (abstract->context, "Unexpected packet flags (%u).", flags); + return DC_STATUS_PROTOCOL; + } + + if (length != len - 8) { + ERROR (abstract->context, "Unexpected packet length (%u " DC_PRINTF_SIZE ").", length, len - 8); + return DC_STATUS_PROTOCOL; + } + + if (msg == INVALID) { + msg = type; + } else if (msg != type) { + ERROR (abstract->context, "Unexpected packet type (%u).", msg); + return DC_STATUS_PROTOCOL; + } + + unsigned short crc = array_uint16_le (packet + len - 2); + unsigned short ccrc = checksum_crc16r_ccitt (packet, len - 2, 0xFFFF, 0xFFFF); + if (crc != ccrc) { + ERROR (abstract->context, "Unexpected packet checksum (%04x %04x).", crc, ccrc); + return DC_STATUS_PROTOCOL; + } + + // Update and emit a progress event. + if (progress) { + progress->current += len - 8; + // Limit the progress to the maximum size. This could happen if the + // dive computer sends more data than requested for some reason. + if (progress->current > progress->maximum) { + WARNING (abstract->context, "Progress exceeds the maximum size."); + progress->current = progress->maximum; + } + device_event_emit (abstract, DC_EVENT_PROGRESS, progress); + } + + if (!dc_buffer_append (buffer, packet + 6, len - 8)) { + ERROR (abstract->context, "Insufficient buffer space available."); + return DC_STATUS_NOMEMORY; + } + + count++; + + if (flags & 0x40) + break; + } + + if (message) + *message = msg; + + return status; +} + +static dc_status_t +divesoft_freedom_transfer (divesoft_freedom_device_t *device, dc_event_progress_t *progress, message_t cmd, const unsigned char data[], size_t size, message_t *msg, dc_buffer_t *buffer) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + device->seqnum++; + + status = divesoft_freedom_send (device, cmd, data, size); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command."); + return status; + } + + status = divesoft_freedom_recv (device, progress, msg, buffer); + if(status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive response."); + return status; + } + + return status; +} + +static dc_status_t +divesoft_freedom_download (divesoft_freedom_device_t *device, message_t cmd, const unsigned char cdata[], size_t csize, unsigned char rdata[], size_t rsize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + + dc_buffer_t *buffer = dc_buffer_new (rsize); + if (buffer == NULL) { + ERROR (abstract->context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + message_t msg = MSG_ECHO; + status = divesoft_freedom_transfer (device, NULL, cmd, cdata, csize, &msg, buffer); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to transfer the packet."); + goto error_free; + } + + if (msg != cmd + 1) { + ERROR (abstract->context, "Unexpected response message (%u).", msg); + status = DC_STATUS_PROTOCOL; + goto error_free; + } + + size_t length = dc_buffer_get_size (buffer); + if (length != rsize) { + ERROR (abstract->context, "Unexpected response length (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", length, rsize); + status = DC_STATUS_PROTOCOL; + goto error_free; + } + + if (rsize) { + memcpy (rdata, dc_buffer_get_data (buffer), rsize); + } + +error_free: + dc_buffer_free (buffer); +error_exit: + return status; +} + +dc_status_t +divesoft_freedom_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +{ + dc_status_t status = DC_STATUS_SUCCESS; + divesoft_freedom_device_t *device = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + device = (divesoft_freedom_device_t *) dc_device_allocate (context, &divesoft_freedom_device_vtable); + if (device == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Set the default values. + device->iostream = NULL; + memset(device->fingerprint, 0, sizeof(device->fingerprint)); + device->seqnum = 0; + + // Setup the HDLC communication. + status = dc_hdlc_open (&device->iostream, context, iostream, 244, 244); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the HDLC stream."); + goto error_free; + } + + // Set the serial communication protocol (115200 8N1). + status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the terminal attributes."); + goto error_free_hdlc; + } + + // Set the timeout for receiving data (3000ms). + status = dc_iostream_set_timeout (device->iostream, 3000); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to set the timeout."); + goto error_free_hdlc; + } + + // Initiate the connection with the dive computer. + const char client[] = "libdivecomputer"; + unsigned char cmd_connect[2 + sizeof(client) - 1] = {0}; + array_uint16_le_set (cmd_connect, COMPRESSION); + memcpy (cmd_connect + 2, client, sizeof(client) - 1); + unsigned char rsp_connect[36] = {0}; + status = divesoft_freedom_download (device, MSG_CONNECT, cmd_connect, sizeof(cmd_connect), rsp_connect, sizeof(rsp_connect)); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to connect to the device."); + goto error_free_hdlc; + } + + DEBUG (context, "Connection: compression=%u, protocol=%u.%u, serial=%.16s", + array_uint16_le (rsp_connect), + rsp_connect[2], rsp_connect[3], + rsp_connect + 4); + + *out = (dc_device_t *) device; + + return DC_STATUS_SUCCESS; + +error_free_hdlc: + dc_iostream_close (device->iostream); +error_free: + dc_device_deallocate ((dc_device_t *) device); + return status; +} + +static dc_status_t +divesoft_freedom_device_close (dc_device_t *abstract) +{ + divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract; + + return dc_iostream_close (device->iostream); +} + +static dc_status_t +divesoft_freedom_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) +{ + divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract; + + if (size && size != sizeof (device->fingerprint)) + return DC_STATUS_INVALIDARGS; + + if (size) + memcpy (device->fingerprint, data, sizeof (device->fingerprint)); + else + memset (device->fingerprint, 0, sizeof (device->fingerprint)); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata) +{ + dc_status_t status = DC_STATUS_SUCCESS; + divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract; + + // Enable progress notifications. + dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER; + device_event_emit (abstract, DC_EVENT_PROGRESS, &progress); + + // Read the device information. + unsigned char rsp_version[26] = {0}; + status = divesoft_freedom_download (device, MSG_VERSION, NULL, 0, rsp_version, sizeof(rsp_version)); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to read the device information."); + goto error_exit; + } + + DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, sw=%u.%u.%u.%u serial=%.16s", + rsp_version[0], + rsp_version[1], rsp_version[2], + rsp_version[3], rsp_version[4], rsp_version[5], + array_uint32_le (rsp_version + 6), + rsp_version + 10); + + // Emit a device info event. + dc_event_devinfo_t devinfo; + devinfo.model = rsp_version[0]; + devinfo.firmware = array_uint24_be (rsp_version + 3); + devinfo.serial = array_convert_str2num (rsp_version + 10 + 5, 11); + device_event_emit(abstract, DC_EVENT_DEVINFO, &devinfo); + + // Allocate memory for the dive list. + dc_buffer_t *divelist = dc_buffer_new (0); + if (divelist == NULL) { + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + // Allocate memory for the download buffer. + dc_buffer_t *buffer = dc_buffer_new (NRECORDS * (4 + FINGERPRINT_SIZE + HEADER_SIZE_V2)); + if (buffer == NULL) { + status = DC_STATUS_NOMEMORY; + goto error_free_divelist; + } + + // Record version and size. + unsigned int version = 0; + unsigned int headersize = 0; + unsigned int recordsize = 0; + + // Download the dive list. + unsigned int ndives = 0; + unsigned int total = 0; + unsigned int maxsize = 0; + unsigned int current = INVALID; + while (1) { + // Clear the buffer. + dc_buffer_clear (buffer); + + // Prepare the command. + unsigned char cmd_list[6] = {0}; + array_uint32_le_set (cmd_list, current); + cmd_list[4] = DIRECTION; + cmd_list[5] = NRECORDS; + + // Download the dive list records. + message_t msg_list = MSG_ECHO; + status = divesoft_freedom_transfer (device, &progress, MSG_DIVE_LIST, cmd_list, sizeof(cmd_list), &msg_list, buffer); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to download the dive list."); + goto error_free_buffer; + } + + // Check the response message type. + if (msg_list != MSG_DIVE_LIST_V1 && msg_list != MSG_DIVE_LIST_V2) { + ERROR (abstract->context, "Unexpected response message (%u).", msg_list); + status = DC_STATUS_PROTOCOL; + goto error_free_buffer; + } + + // Store/check the version. + if (version == 0) { + version = msg_list; + headersize = version == MSG_DIVE_LIST_V1 ? + HEADER_SIZE_V1 : HEADER_SIZE_V2; + recordsize = 4 + FINGERPRINT_SIZE + headersize; + } else if (version != msg_list) { + ERROR (abstract->context, "Unexpected response message (%u).", msg_list); + status = DC_STATUS_PROTOCOL; + goto error_free_buffer; + } + + const unsigned char *data = dc_buffer_get_data (buffer); + size_t size = dc_buffer_get_size (buffer); + + // Process the records. + size_t offset = 0, count = 0; + while (offset + recordsize <= size) { + // Get the record data. + unsigned int handle = array_uint32_le (data + offset); + const unsigned char *fingerprint = data + offset + 4; + const unsigned char *header = data + offset + 4 + FINGERPRINT_SIZE; + + // Check the fingerprint data. + if (memcmp (device->fingerprint, fingerprint, sizeof(device->fingerprint)) == 0) { + break; + } + + // Get the length of the dive. + unsigned int nrecords = version == MSG_DIVE_LIST_V1 ? + array_uint32_le (header + 16) & 0x3FFFF : + array_uint32_le (header + 20); + unsigned int length = headersize + nrecords * RECORD_SIZE; + + // Calculate the total and maximum size. + if (length > maxsize) + maxsize = length; + total += length; + + // Set the handle for the next request. + current = handle; + + offset += recordsize; + count++; + ndives++; + } + + // Append the records to the dive list buffer. + if (!dc_buffer_append (divelist, data, count * recordsize)) { + ERROR (abstract->context, "Insufficient buffer space available."); + status = DC_STATUS_NOMEMORY; + goto error_free_buffer; + } + + // Stop downloading if there are no more records. + if (count < NRECORDS) + break; + } + + // Update and emit a progress event. + progress.maximum = progress.current + total; + device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); + + // Reserve memory for the largest dive. + dc_buffer_reserve (buffer, maxsize); + + const unsigned char *data = dc_buffer_get_data (divelist); + size_t size = dc_buffer_get_size (divelist); + + size_t offset = 0; + while (offset + recordsize <= size) { + // Get the record data. + unsigned int handle = array_uint32_le (data + offset); + const unsigned char *fingerprint = data + offset + 4; + const unsigned char *header = data + offset + 4 + FINGERPRINT_SIZE; + + // Get the length of the dive. + unsigned int nrecords = version == MSG_DIVE_LIST_V1 ? + array_uint32_le (header + 16) & 0x3FFFF : + array_uint32_le (header + 20); + unsigned int length = headersize + nrecords * RECORD_SIZE; + + // Clear the buffer. + dc_buffer_clear (buffer); + + // Prepare the command. + unsigned char cmd_dive[12] = {0}; + array_uint32_le_set (cmd_dive + 0, handle); + array_uint32_le_set (cmd_dive + 4, 0); + array_uint32_le_set (cmd_dive + 8, length); + + // Download the dive. + message_t msg_dive = MSG_ECHO; + status = divesoft_freedom_transfer (device, &progress, MSG_DIVE_DATA, cmd_dive, sizeof(cmd_dive), &msg_dive, buffer); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to download the dive."); + goto error_free_buffer; + } + + // Check the response message type. + if (msg_dive != MSG_DIVE_DATA_RSP) { + ERROR (abstract->context, "Unexpected response message (%u).", msg_dive); + status = DC_STATUS_PROTOCOL; + goto error_free_buffer; + } + + // Verify both dive headers are identical. + if (dc_buffer_get_size (buffer) < headersize || + memcmp (header, dc_buffer_get_data (buffer), headersize) != 0) { + ERROR (abstract->context, "Unexpected profile header."); + status = DC_STATUS_PROTOCOL; + goto error_free_buffer; + } + + if (callback && !callback (dc_buffer_get_data(buffer), dc_buffer_get_size(buffer), fingerprint, sizeof (device->fingerprint), userdata)) { + break; + } + + offset += recordsize; + } + +error_free_buffer: + dc_buffer_free (buffer); +error_free_divelist: + dc_buffer_free (divelist); +error_exit: + return status; +} diff --git a/src/divesoft_freedom.h b/src/divesoft_freedom.h new file mode 100644 index 0000000..db04edb --- /dev/null +++ b/src/divesoft_freedom.h @@ -0,0 +1,43 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jan Matoušek, Jef Driesen + * + * 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 DIVESOFT_FREEDOM_H +#define DIVESOFT_FREEDOM_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +dc_status_t +divesoft_freedom_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); + +dc_status_t +divesoft_freedom_parser_create (dc_parser_t **parser, dc_context_t *context); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DIVESOFT_FREEDOM_H */ diff --git a/src/divesoft_freedom_parser.c b/src/divesoft_freedom_parser.c new file mode 100644 index 0000000..b595e63 --- /dev/null +++ b/src/divesoft_freedom_parser.c @@ -0,0 +1,1070 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jan Matoušek, Jef Driesen + * + * 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 +#include + +#include + +#include "divesoft_freedom.h" +#include "context-private.h" +#include "parser-private.h" +#include "checksum.h" +#include "array.h" + +#define UNDEFINED 0xFFFFFFFF + +#define EPOCH 946684800 // 2000-01-01 00:00:00 UTC + +#define OC 0 +#define OXYGEN 1 +#define DILUENT 2 + +#define NSENSORS 4 +#define NGASMIXES 12 +#define NTANKS 12 + +#define HEADER_SIGNATURE_V1 0x45766944 // "DivE" +#define HEADER_SIGNATURE_V2 0x45566944 // "DiVE" + +#define HEADER_SIZE_V1 32 +#define HEADER_SIZE_V2 64 + +#define RECORD_SIZE 16 + +#define SEAWATER 1028 +#define FRESHWATER 1000 + +typedef enum logrecord_t { + LREC_POINT = 0, + LREC_MANIPULATION = 1, + LREC_AUTO = 2, + LREC_DIVER_ERROR = 3, + LREC_INTERNAL_ERROR = 4, + LREC_ACTIVITY = 5, + LREC_CONFIGURATION = 6, + LREC_MEASURE = 7, + LREC_STATE = 8, + LREC_INFO = 9, +} logrecord_t; + +typedef enum point_id_t { + POINT_1 = 0, + POINT_2 = 1, + POINT_1_OLD = 0x3FF, +} point_id_t; + +typedef enum configuration_id_t { + CFG_ID_TEST_CCR_FULL = 0, + CFG_ID_TEST_CCR_PARTIAL = 1, + CFG_ID_OXYGEN_CALIBRATION = 2, + CFG_ID_SERIAL = 3, + CFG_ID_DECO = 4, + CFG_ID_VERSION = 5, + CFG_ID_ASCENT = 6, + CFG_ID_AI = 7, + CFG_ID_CCR = 8, + CFG_ID_DILUENTS = 9, +} configuration_id_t; + +typedef enum measure_id_t { + MEASURE_ID_OXYGEN = 0, + MEASURE_ID_BATTERY = 1, + MEASURE_ID_HELIUM = 2, + MEASURE_ID_OXYGEN_MV = 3, + MEASURE_ID_GPS = 4, + MEASURE_ID_PRESSURE = 5, + MEASURE_ID_AI_SAC = 6, + MEASURE_ID_AI_PRESSURE = 7, + MEASURE_ID_BRIGHTNESS = 8, + MEASURE_ID_AI_STAT = 9, +} measure_id_t; + +typedef enum state_id_t { + STATE_ID_DECO_N2LOW = 0, + STATE_ID_DECO_N2HIGH = 1, + STATE_ID_DECO_HELOW = 2, + STATE_ID_DECO_HEHIGH = 3, + STATE_ID_PLAN_STEPS = 4, +} state_id_t; + +typedef enum event_t { + EVENT_DUMMY = 0, + EVENT_SETPOINT_MANUAL = 1, + EVENT_SETPOINT_AUTO = 2, + EVENT_OC = 3, + EVENT_CCR = 4, + EVENT_MIX_CHANGED = 5, + EVENT_START = 6, + EVENT_TOO_FAST = 7, + EVENT_ABOVE_CEILING = 8, + EVENT_TOXIC = 9, + EVENT_HYPOX = 10, + EVENT_CRITICAL = 11, + EVENT_SENSOR_DISABLED = 12, + EVENT_SENSOR_ENABLED = 13, + EVENT_O2_BACKUP = 14, + EVENT_PEER_DOWN = 15, + EVENT_HS_DOWN = 16, + EVENT_INCONSISTENT = 17, + EVENT_KEYDOWN = 18, + EVENT_SCR = 19, + EVENT_ABOVE_STOP = 20, + EVENT_SAFETY_MISS = 21, + EVENT_FATAL = 22, + EVENT_DILUENT = 23, + EVENT_CHANGE_MODE = 24, + EVENT_SOLENOID = 25, + EVENT_BOOKMARK = 26, + EVENT_GF_SWITCH = 27, + EVENT_PEER_UP = 28, + EVENT_HS_UP = 29, + EVENT_CNS = 30, + EVENT_BATTERY_LOW = 31, + EVENT_PPO2_LOST = 32, + EVENT_SENSOR_VALUE_BAD = 33, + EVENT_SAFETY_STOP_END = 34, + EVENT_DECO_STOP_END = 35, + EVENT_DEEP_STOP_END = 36, + EVENT_NODECO_END = 37, + EVENT_DEPTH_REACHED = 38, + EVENT_TIME_ELAPSED = 39, + EVENT_STACK_USAGE = 40, + EVENT_GAS_SWITCH_INFO = 41, + EVENT_PRESSURE_SENS_WARN = 42, + EVENT_PRESSURE_SENS_FAIL = 43, + EVENT_CHECK_O2_SENSORS = 44, + EVENT_SWITCH_TO_COMP_SCR = 45, + EVENT_GAS_LOST = 46, + EVENT_AIRBREAK = 47, + EVENT_AIRBREAK_END = 48, + EVENT_AIRBREAK_MISSED = 49, + EVENT_BORMT_EXPIRATION = 50, + EVENT_BORMT_EXPIRED = 51, + EVENT_SENSOR_EXCLUDED = 52, + EVENT_PREBR_SKIPPED = 53, + EVENT_BOCCR_BORMT_EXPIRED = 54, + EVENT_WAYPOINT = 55, + EVENT_TURNAROUND = 56, + EVENT_SOLENOID_FAILURE = 57, + EVENT_SM_CYL_PRESS_DIFF = 58, + EVENT_BAILOUT_MOD_EXCEEDED = 59, +} event_t; + +typedef enum divemode_t { + STMODE_UNKNOWN = 0, + STMODE_OC = 1, + STMODE_CCR = 2, + STMODE_MCCR = 3, + STMODE_FREE = 4, + STMODE_GAUGE = 5, + STMODE_ASCR = 6, + STMODE_PSCR = 7, + STMODE_BOCCR = 8, +} divemode_t; + +typedef enum setpoint_change_t { + SP_MANUAL = 0, + SP_AUTO_START = 1, + SP_AUTO_HYPOX = 2, + SP_AUTO_TIMEOUT = 3, + SP_AUTO_ASCENT = 4, + SP_AUTO_STALL = 5, + SP_AUTO_SPLOW = 6, + SP_AUTO_DEPTH_DESC = 7, + SP_AUTO_DEPTH_ASC = 8, +} setpoint_change_t; + +typedef enum sensor_state_t { + SENSTAT_NORMAL = 0, + SENSTAT_OVERRANGE = 1, + SENSTAT_DISABLED = 2, + SENSTAT_EXCLUDED = 3, + SENSTAT_UNCALIBRATED = 4, + SENSTAT_ERROR = 5, + SENSTAT_OFFLINE = 6, + SENSTAT_INHIBITED = 7, + SENSTAT_NOT_EXIST = 8, +} sensor_state_t; + +typedef enum battery_state_t { + BATSTATE_NO_BATTERY = 0, + BATSTATE_UNKNOWN = 1, + BATSTATE_DISCHARGING = 2, + BATSTATE_CHARGING = 3, + BATSTATE_FULL = 4, +} battery_state_t; + +typedef struct divesoft_freedom_gasmix_t { + unsigned int oxygen; + unsigned int helium; + unsigned int type; + unsigned int id; +} divesoft_freedom_gasmix_t; + +typedef struct divesoft_freedom_tank_t { + unsigned int volume; + unsigned int workpressure; + unsigned int beginpressure; + unsigned int endpressure; + unsigned int transmitter; + unsigned int active; +} divesoft_freedom_tank_t; + +typedef struct divesoft_freedom_parser_t { + dc_parser_t base; + // Cached fields. + unsigned int cached; + unsigned int version; + unsigned int headersize; + unsigned int divetime; + unsigned int divemode; + int temperature_min; + unsigned int maxdepth; + unsigned int atmospheric; + unsigned int avgdepth; + unsigned int ngasmixes; + divesoft_freedom_gasmix_t gasmix[NGASMIXES]; + unsigned int diluent; + unsigned int ntanks; + divesoft_freedom_tank_t tank[NTANKS]; + unsigned int vpm; + unsigned int gf_lo; + unsigned int gf_hi; + unsigned int seawater; + unsigned int calibration[NSENSORS]; + unsigned int calibrated; +} divesoft_freedom_parser_t; + +static dc_status_t divesoft_freedom_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); +static dc_status_t divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); +static dc_status_t divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); +static dc_status_t divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); + +static const dc_parser_vtable_t divesoft_freedom_parser_vtable = { + sizeof(divesoft_freedom_parser_t), + DC_FAMILY_DIVESOFT_FREEDOM, + divesoft_freedom_parser_set_data, /* set_data */ + NULL, /* set_clock */ + NULL, /* set_atmospheric */ + NULL, /* set_density */ + divesoft_freedom_parser_get_datetime, /* datetime */ + divesoft_freedom_parser_get_field, /* fields */ + divesoft_freedom_parser_samples_foreach, /* samples_foreach */ + NULL /* destroy */ +}; + +static unsigned int +divesoft_freedom_find_gasmix (divesoft_freedom_gasmix_t gasmix[], unsigned int count, unsigned int oxygen, unsigned int helium, unsigned int type) +{ + unsigned int i = 0; + while (i < count) { + if (oxygen == gasmix[i].oxygen && + helium == gasmix[i].helium && + type == gasmix[i].type) + break; + i++; + } + + return i; +} + +static unsigned int +divesoft_freedom_find_tank (divesoft_freedom_tank_t tank[], unsigned int count, unsigned int transmitter) +{ + unsigned int i = 0; + while (i < count) { + if (transmitter == tank[i].transmitter) + break; + i++; + } + + return i; +} + +static unsigned int +divesoft_freedom_is_ccr (unsigned int divemode) +{ + return divemode == STMODE_CCR || divemode == STMODE_MCCR || + divemode == STMODE_ASCR || divemode == STMODE_PSCR || + divemode == STMODE_BOCCR; +} + +static dc_status_t +divesoft_freedom_cache (divesoft_freedom_parser_t *parser) +{ + dc_parser_t *abstract = (dc_parser_t *) parser; + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (parser->cached) { + return DC_STATUS_SUCCESS; + } + + unsigned int headersize = 4; + if (size < headersize) { + ERROR (abstract->context, "Unexpected header size (%u).", size); + return DC_STATUS_DATAFORMAT; + } + + unsigned int version = array_uint32_le (data); + if (version == HEADER_SIGNATURE_V1) { + headersize = HEADER_SIZE_V1; + } else if (version == HEADER_SIGNATURE_V2) { + headersize = HEADER_SIZE_V2; + } else { + ERROR (abstract->context, "Unexpected header version (%08x).", version); + return DC_STATUS_DATAFORMAT; + } + + if (size < headersize) { + ERROR (abstract->context, "Unexpected header size (%u).", size); + return DC_STATUS_DATAFORMAT; + } + + unsigned short crc = array_uint16_le (data + 4); + unsigned short ccrc = checksum_crc16r_ansi (data + 6, headersize - 6, 0xFFFF, 0x0000); + if (crc != ccrc) { + ERROR (abstract->context, "Invalid header checksum (%04x %04x).", crc, ccrc); + return DC_STATUS_DATAFORMAT; + } + + // Parse the dive header. + unsigned int divetime = 0; + unsigned int divemode = 0; + unsigned int temperature_min = 0; + unsigned int maxdepth = 0; + unsigned int atmospheric = 0; + unsigned int avgdepth = 0; + unsigned int diluent_o2 = 0, diluent_he = 0; + if (version == HEADER_SIGNATURE_V1) { + unsigned int misc1 = array_uint32_le (data + 12); + unsigned int misc2 = array_uint32_le (data + 16); + divetime = misc1 & 0x1FFFF; + divemode = (misc1 & 0x38000000) >> 27; + temperature_min = (signed int) signextend ((misc2 & 0xFFC0000) >> 18, 10); + maxdepth = array_uint16_le (data + 20); + atmospheric = array_uint16_le (data + 24); + avgdepth = 0; + diluent_o2 = data[26]; + diluent_he = data[27]; + } else { + divetime = array_uint32_le (data + 12); + divemode = data[18]; + temperature_min = (signed short) array_uint16_le (data + 24); + maxdepth = array_uint16_le (data + 28); + atmospheric = array_uint16_le (data + 32); + avgdepth = array_uint16_le (data + 38); + diluent_o2 = 0; + diluent_he = 0; + + DEBUG (abstract->context, "Device: serial=%.4s-%.8s", + data + 52, data + 56); + } + + divesoft_freedom_gasmix_t gasmix_ai[NGASMIXES] = {0}, + gasmix_diluent[NGASMIXES] = {0}, + gasmix_event[NGASMIXES] = {0}; + unsigned int ngasmix_ai = 0, + ngasmix_diluent = 0, + ngasmix_event = 0; + divesoft_freedom_tank_t tank[NTANKS] = {0}; + unsigned int ntanks = 0; + + unsigned int vpm = 0, gf_lo = 0, gf_hi = 0; + unsigned int seawater = 0; + unsigned int calibration[NSENSORS] = {0}; + unsigned int calibrated = 0; + + unsigned int gasmixid_previous = UNDEFINED; + + + // Parse the dive profile. + unsigned int offset = headersize; + while (offset + RECORD_SIZE <= size) { + if (array_isequal(data + offset, RECORD_SIZE, 0xFF)) { + WARNING (abstract->context, "Skipping empty sample."); + offset += RECORD_SIZE; + continue; + } + + unsigned int flags = array_uint32_le (data + offset); + unsigned int type = (flags & 0x0000000F) >> 0; + unsigned int id = (flags & 0x7FE00000) >> 21; + + if (type == LREC_CONFIGURATION) { + // Configuration record. + if (id == CFG_ID_DECO) { + unsigned int misc = array_uint16_le (data + offset + 4); + gf_lo = data[offset + 8]; + gf_hi = data[offset + 9]; + seawater = misc & 0x02; + vpm = misc & 0x20; + } else if (id == CFG_ID_VERSION) { + DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, sw=%u.%u.%u.%u flags=%u", + data[offset + 4], + data[offset + 5], data[offset + 6], + data[offset + 7], data[offset + 8], data[offset + 9], + array_uint32_le (data + offset + 12), + array_uint16_le (data + offset + 10)); + } else if (id == CFG_ID_SERIAL) { + DEBUG (abstract->context, "Device: serial=%.4s-%.8s", + data + offset + 4, data + offset + 8); + } else if (id == CFG_ID_DILUENTS) { + for (unsigned int i = 0; i < 4; ++i) { + unsigned int o2 = data[offset + 4 + i * 3 + 0]; + unsigned int he = data[offset + 4 + i * 3 + 1]; + unsigned int state = data[offset + 4 + i * 3 + 2]; + if (state & 0x01) { + if (ngasmix_diluent >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_NOMEMORY; + } + gasmix_diluent[ngasmix_diluent].oxygen = o2; + gasmix_diluent[ngasmix_diluent].helium = he; + gasmix_diluent[ngasmix_diluent].type = DILUENT; + gasmix_diluent[ngasmix_diluent].id = (state & 0xFE) >> 1; + ngasmix_diluent++; + } + } + } else if (id == CFG_ID_OXYGEN_CALIBRATION) { + for (unsigned int i = 0; i < NSENSORS; ++i) { + calibration[i] = array_uint16_le (data + offset + 4 + i * 2); + } + calibrated = 1; + } else if (id == CFG_ID_AI) { + unsigned int o2 = data[offset + 4]; + unsigned int he = data[offset + 5]; + unsigned int volume = array_uint16_le (data + offset + 6); + unsigned int workpressure = array_uint16_le (data + offset + 8); + unsigned int transmitter = data[offset + 10]; + unsigned int gasmixid = data[offset + 11]; + + // Workaround for a bug in some pre-release firmware versions, + // where the ID of the CCR gas mixes (oxygen and diluent) is + // not stored correctly. + if (gasmixid < 10 && gasmixid <= gasmixid_previous && gasmixid_previous != UNDEFINED) { + WARNING (abstract->context, "Fixed the CCR gas mix id (%u -> %u) for tank %u.", + gasmixid, gasmixid + 10, ntanks); + gasmixid += 10; + } + gasmixid_previous = gasmixid; + + // Add the gas mix. + if (ngasmix_ai >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_NOMEMORY; + } + gasmix_ai[ngasmix_ai].oxygen = o2; + gasmix_ai[ngasmix_ai].helium = he; + if (gasmixid == 10) { + gasmix_ai[ngasmix_ai].type = OXYGEN; + } else if (gasmixid == 11) { + gasmix_ai[ngasmix_ai].type = DILUENT; + } else { + gasmix_ai[ngasmix_ai].type = OC; + } + gasmix_ai[ngasmix_ai].id = gasmixid; + ngasmix_ai++; + + // Add the tank. + if (ntanks >= NTANKS) { + ERROR (abstract->context, "Maximum number of tanks reached."); + return DC_STATUS_NOMEMORY; + } + tank[ntanks].volume = volume; + tank[ntanks].workpressure = workpressure; + tank[ntanks].transmitter = transmitter; + ntanks++; + } + } else if ((type >= LREC_MANIPULATION && type <= LREC_ACTIVITY) || type == LREC_INFO) { + // Event record. + unsigned int event = array_uint16_le (data + offset + 4); + + if (event == EVENT_MIX_CHANGED || event == EVENT_DILUENT || event == EVENT_CHANGE_MODE) { + unsigned int o2 = data[offset + 6]; + unsigned int he = data[offset + 7]; + unsigned int mixtype = OC; + if (event == EVENT_DILUENT) { + mixtype = DILUENT; + } else if (event == EVENT_CHANGE_MODE) { + unsigned int mode = data[offset + 8]; + if (divesoft_freedom_is_ccr (mode)) { + mixtype = DILUENT; + } + } + + unsigned int idx = divesoft_freedom_find_gasmix (gasmix_event, ngasmix_event, o2, he, mixtype); + if (idx >= ngasmix_event) { + if (ngasmix_event >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_NOMEMORY; + } + gasmix_event[ngasmix_event].oxygen = o2; + gasmix_event[ngasmix_event].helium = he; + gasmix_event[ngasmix_event].type = mixtype; + gasmix_event[ngasmix_event].id = UNDEFINED; + ngasmix_event++; + } + } + } else if (type == LREC_MEASURE) { + // Measurement record. + if (id == MEASURE_ID_AI_PRESSURE) { + for (unsigned int i = 0; i < NTANKS; ++i) { + unsigned int pressure = data[offset + 4 + i]; + if (pressure == 0 || pressure == 0xFF) + continue; + + unsigned int idx = divesoft_freedom_find_tank (tank, ntanks, i); + if (idx >= ntanks) { + ERROR (abstract->context, "Tank %u not found.", idx); + return DC_STATUS_DATAFORMAT; + } + + if (!tank[idx].active) { + tank[idx].active = 1; + tank[idx].beginpressure = pressure; + tank[idx].endpressure = pressure; + } + tank[idx].endpressure = pressure; + } + } + } + + offset += RECORD_SIZE; + } + + unsigned int ngasmixes = 0; + divesoft_freedom_gasmix_t gasmix[NGASMIXES] = {0}; + unsigned int diluent = UNDEFINED; + + // Add the gas mixes from the AI integration records. + for (unsigned int i = 0; i < ngasmix_ai; ++i) { + gasmix[ngasmixes] = gasmix_ai[i]; + ngasmixes++; + } + + // Add the gas mixes from the diluent records. + for (unsigned int i = 0; i < ngasmix_diluent; ++i) { + unsigned int idx = divesoft_freedom_find_gasmix (gasmix, ngasmixes, + gasmix_diluent[i].oxygen, gasmix_diluent[i].helium, gasmix_diluent[i].type); + if (idx >= ngasmixes) { + if (ngasmixes >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_NOMEMORY; + } + gasmix[ngasmixes] = gasmix_diluent[i]; + ngasmixes++; + } + } + + // Add the initial diluent. + if (divesoft_freedom_is_ccr (divemode) && + (diluent_o2 != 0 || diluent_he != 0)) { + unsigned int idx = divesoft_freedom_find_gasmix (gasmix, ngasmixes, + diluent_o2, diluent_he, DILUENT); + if (idx >= ngasmixes) { + if (ngasmixes >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_NOMEMORY; + } + gasmix[ngasmixes].oxygen = diluent_o2; + gasmix[ngasmixes].helium = diluent_he; + gasmix[ngasmixes].type = DILUENT; + gasmix[ngasmixes].id = UNDEFINED; + ngasmixes++; + } + + // Index of the initial diluent. + diluent = idx; + } + + // Add the gas mixes from the gas change events. + for (unsigned int i = 0; i < ngasmix_event; ++i) { + unsigned int idx = divesoft_freedom_find_gasmix (gasmix, ngasmixes, + gasmix_event[i].oxygen, gasmix_event[i].helium, gasmix_event[i].type); + if (idx >= ngasmixes) { + if (ngasmixes >= NGASMIXES) { + ERROR (abstract->context, "Maximum number of gas mixes reached."); + return DC_STATUS_NOMEMORY; + } + gasmix[ngasmixes] = gasmix_event[i]; + ngasmixes++; + } + } + + // Cache the data for later use. + parser->cached = 1; + parser->version = version; + parser->headersize = headersize; + parser->divetime = divetime; + parser->divemode = divemode; + parser->temperature_min = temperature_min; + parser->maxdepth = maxdepth; + parser->atmospheric = atmospheric; + parser->avgdepth = avgdepth; + parser->ngasmixes = ngasmixes; + for (unsigned int i = 0; i < ngasmixes; ++i) { + parser->gasmix[i] = gasmix[i]; + } + parser->diluent = diluent; + parser->ntanks = ntanks; + for (unsigned int i = 0; i < ntanks; ++i) { + parser->tank[i] = tank[i]; + } + parser->vpm = vpm; + parser->gf_lo = gf_lo; + parser->gf_hi = gf_hi; + parser->seawater = seawater; + for (unsigned int i = 0; i < NSENSORS; ++i) { + parser->calibration[i] = calibration[i]; + } + parser->calibrated = calibrated; + + return DC_STATUS_SUCCESS; +} + +dc_status_t +divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context) +{ + divesoft_freedom_parser_t *parser = NULL; + + if (out == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + parser = (divesoft_freedom_parser_t *) dc_parser_allocate (context, &divesoft_freedom_parser_vtable); + if (parser == NULL) { + ERROR (context, "Failed to allocate memory."); + return DC_STATUS_NOMEMORY; + } + + // Set the default values. + parser->cached = 0; + parser->version = 0; + parser->headersize = 0; + parser->divetime = 0; + parser->divemode = 0; + parser->temperature_min = 0; + parser->maxdepth = 0; + parser->atmospheric = 0; + parser->avgdepth = 0; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->gasmix[i].oxygen = 0; + parser->gasmix[i].helium = 0; + parser->gasmix[i].type = 0; + parser->gasmix[i].id = 0; + } + parser->diluent = UNDEFINED; + parser->ntanks = 0; + for (unsigned int i = 0; i < NTANKS; ++i) { + parser->tank[i].volume = 0; + parser->tank[i].workpressure = 0; + parser->tank[i].beginpressure = 0; + parser->tank[i].endpressure = 0; + parser->tank[i].transmitter = 0; + parser->tank[i].active = 0; + } + parser->vpm = 0; + parser->gf_lo = 0; + parser->gf_hi = 0; + parser->seawater = 0; + for (unsigned int i = 0; i < NSENSORS; ++i) { + parser->calibration[i] = 0; + } + parser->calibrated = 0; + + *out = (dc_parser_t *) parser; + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +divesoft_freedom_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) +{ + divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract; + + // Reset the cache. + parser->cached = 0; + parser->version = 0; + parser->headersize = 0; + parser->divetime = 0; + parser->divemode = 0; + parser->temperature_min = 0; + parser->maxdepth = 0; + parser->atmospheric = 0; + parser->avgdepth = 0; + parser->ngasmixes = 0; + for (unsigned int i = 0; i < NGASMIXES; ++i) { + parser->gasmix[i].oxygen = 0; + parser->gasmix[i].helium = 0; + parser->gasmix[i].type = 0; + parser->gasmix[i].id = 0; + } + parser->diluent = UNDEFINED; + parser->ntanks = 0; + for (unsigned int i = 0; i < NTANKS; ++i) { + parser->tank[i].volume = 0; + parser->tank[i].workpressure = 0; + parser->tank[i].beginpressure = 0; + parser->tank[i].endpressure = 0; + parser->tank[i].transmitter = 0; + parser->tank[i].active = 0; + } + parser->vpm = 0; + parser->gf_lo = 0; + parser->gf_hi = 0; + parser->seawater = 0; + for (unsigned int i = 0; i < NSENSORS; ++i) { + parser->calibration[i] = 0; + } + parser->calibrated = 0; + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) +{ + dc_status_t status = DC_STATUS_SUCCESS; + divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract; + const unsigned char *data = abstract->data; + + // Cache the header data. + status = divesoft_freedom_cache (parser); + if (status != DC_STATUS_SUCCESS) + return status; + + unsigned int timestamp = array_uint32_le (data + 8); + dc_ticks_t ticks = (dc_ticks_t) timestamp + EPOCH; + + 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; + } else { + datetime->timezone = DC_TIMEZONE_NONE; + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) +{ + dc_status_t status = DC_STATUS_SUCCESS; + divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract; + + // Cache the header data. + status = divesoft_freedom_cache (parser); + if (status != DC_STATUS_SUCCESS) + return status; + + dc_salinity_t *water = (dc_salinity_t *) value; + dc_gasmix_t *gasmix = (dc_gasmix_t *) value; + dc_tank_t *tank = (dc_tank_t *) value; + dc_decomodel_t *decomodel = (dc_decomodel_t *) value; + + if (value) { + switch (type) { + case DC_FIELD_DIVETIME: + *((unsigned int *) value) = parser->divetime; + break; + case DC_FIELD_MAXDEPTH: + *((double *) value) = parser->maxdepth / 100.0; + break; + case DC_FIELD_AVGDEPTH: + if (parser->version != HEADER_SIGNATURE_V2) + return DC_STATUS_UNSUPPORTED; + *((double *) value) = parser->avgdepth / 100.0; + break; + case DC_FIELD_TEMPERATURE_MINIMUM: + *((double *) value) = parser->temperature_min / 10.0; + break; + case DC_FIELD_ATMOSPHERIC: + *((double *) value) = parser->atmospheric * 10.0 / BAR; + break; + case DC_FIELD_SALINITY: + water->type = parser->seawater ? DC_WATER_SALT : DC_WATER_FRESH; + water->density = parser->seawater ? SEAWATER : FRESHWATER; + break; + case DC_FIELD_DIVEMODE: + switch (parser->divemode) { + case STMODE_OC: + *((dc_divemode_t *) value) = DC_DIVEMODE_OC; + break; + case STMODE_CCR: + case STMODE_MCCR: + case STMODE_BOCCR: + *((dc_divemode_t *) value) = DC_DIVEMODE_CCR; + break; + case STMODE_FREE: + *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; + break; + case STMODE_GAUGE: + *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; + break; + case STMODE_ASCR: + case STMODE_PSCR: + *((dc_divemode_t *) value) = DC_DIVEMODE_SCR; + break; + case STMODE_UNKNOWN: + return DC_STATUS_UNSUPPORTED; + default: + return DC_STATUS_DATAFORMAT; + } + break; + case DC_FIELD_GASMIX_COUNT: + *((unsigned int *) value) = parser->ngasmixes; + break; + case DC_FIELD_GASMIX: + gasmix->helium = parser->gasmix[flags].helium / 100.0; + gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; + gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; + break; + case DC_FIELD_TANK_COUNT: + *((unsigned int *) value) = parser->ntanks; + break; + case DC_FIELD_TANK: + if (parser->tank[flags].volume > 990 || + parser->tank[flags].workpressure > 400) { + tank->type = DC_TANKVOLUME_NONE; + tank->volume = 0.0; + tank->workpressure = 0.0; + } else { + tank->type = DC_TANKVOLUME_METRIC; + tank->volume = parser->tank[flags].volume / 10.0; + tank->workpressure = parser->tank[flags].workpressure; + } + tank->beginpressure = parser->tank[flags].beginpressure * 2.0; + tank->endpressure = parser->tank[flags].endpressure * 2.0; + tank->gasmix = flags; + break; + case DC_FIELD_DECOMODEL: + if (parser->vpm) { + decomodel->type = DC_DECOMODEL_VPM; + decomodel->conservatism = 0; + } else { + decomodel->type = DC_DECOMODEL_BUHLMANN; + decomodel->conservatism = 0; + decomodel->params.gf.low = parser->gf_lo; + decomodel->params.gf.high = parser->gf_hi; + } + break; + default: + return DC_STATUS_UNSUPPORTED; + } + } + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) +{ + dc_status_t status = DC_STATUS_SUCCESS; + divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract; + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + // Cache the header data. + status = divesoft_freedom_cache (parser); + if (status != DC_STATUS_SUCCESS) + return status; + + unsigned int time = UNDEFINED; + unsigned int initial = 0; + unsigned int offset = parser->headersize; + while (offset + RECORD_SIZE <= size) { + dc_sample_value_t sample = {0}; + + if (array_isequal(data + offset, RECORD_SIZE, 0xFF)) { + WARNING (abstract->context, "Skipping empty sample."); + offset += RECORD_SIZE; + continue; + } + + unsigned int flags = array_uint32_le (data + offset); + unsigned int type = (flags & 0x0000000F) >> 0; + unsigned int timestamp = (flags & 0x001FFFF0) >> 4; + unsigned int id = (flags & 0x7FE00000) >> 21; + + if (timestamp != time) { + if (timestamp < time && time != UNDEFINED) { + // The timestamp are supposed to be monotonically increasing, + // but occasionally there are small jumps back in time with just + // 1 or 2 seconds. To get back in sync, those samples are + // skipped. Larger jumps are treated as errors. + if (time - timestamp > 5) { + ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time); + return DC_STATUS_DATAFORMAT; + } + WARNING (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time); + offset += RECORD_SIZE; + continue; + } + time = timestamp; + sample.time = time; + if (callback) callback(DC_SAMPLE_TIME, sample, userdata); + } + + // Initial diluent. + if (!initial) { + if (parser->diluent != UNDEFINED) { + sample.gasmix = parser->diluent; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + } + initial = 1; + } + + if (type == LREC_POINT) { + // General log record. + unsigned int depth = array_uint16_le (data + offset + 4); + unsigned int ppo2 = array_uint16_le (data + offset + 6); + + sample.depth = depth / 100.0; + if (callback) callback(DC_SAMPLE_DEPTH, sample, userdata); + + if (ppo2) { + sample.ppo2 = ppo2 * 10.0 / BAR; + if (callback) callback(DC_SAMPLE_PPO2, sample, userdata); + } + + if (id == POINT_2) { + unsigned int orientation = array_uint32_le (data + offset + 8); + unsigned int heading = orientation & 0x1FF; + sample.bearing = heading; + if (callback) callback (DC_SAMPLE_BEARING, sample, userdata); + } else if (id == POINT_1 || id == POINT_1_OLD) { + unsigned int misc = array_uint32_le (data + offset + 8); + unsigned int ceiling = array_uint16_le (data + offset + 12); + unsigned int setpoint = data[offset + 15]; + unsigned int ndl = (misc & 0x000003FF); + unsigned int tts = (misc & 0x000FFC00) >> 10; + unsigned int temp = (misc & 0x3FF00000) >> 20; + + // Temperature + sample.temperature = (signed int) signextend (temp, 10) / 10.0; + if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata); + + // Deco / NDL + if (ceiling) { + sample.deco.type = DC_DECO_DECOSTOP; + sample.deco.time = 0; + sample.deco.depth = ceiling / 100.0; + } else { + sample.deco.type = DC_DECO_NDL; + sample.deco.time = ndl * 60; + sample.deco.depth = 0.0; + } + if (callback) callback(DC_SAMPLE_DECO, sample, userdata); + + // Setpoint + if (setpoint) { + sample.setpoint = setpoint / 100.0; + if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata); + } + } + } else if ((type >= LREC_MANIPULATION && type <= LREC_ACTIVITY) || type == LREC_INFO) { + // Event record. + unsigned int event = array_uint16_le (data + offset + 4); + + if (event == EVENT_BOOKMARK) { + sample.event.type = SAMPLE_EVENT_BOOKMARK; + sample.event.time = 0; + sample.event.flags = 0; + sample.event.value = 0; + if (callback) callback(DC_SAMPLE_EVENT, sample, userdata); + } else if (event == EVENT_MIX_CHANGED || event == EVENT_DILUENT || event == EVENT_CHANGE_MODE) { + unsigned int o2 = data[offset + 6]; + unsigned int he = data[offset + 7]; + unsigned int mixtype = OC; + if (event == EVENT_DILUENT) { + mixtype = DILUENT; + } else if (event == EVENT_CHANGE_MODE) { + unsigned int mode = data[offset + 8]; + if (divesoft_freedom_is_ccr (mode)) { + mixtype = DILUENT; + } + } + + unsigned int idx = divesoft_freedom_find_gasmix (parser->gasmix, parser->ngasmixes, o2, he, mixtype); + if (idx >= parser->ngasmixes) { + ERROR (abstract->context, "Gas mix (%u/%u) not found.", o2, he); + return DC_STATUS_DATAFORMAT; + } + sample.gasmix = idx; + if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); + } else if (event == EVENT_CNS) { + sample.cns = array_uint16_le (data + offset + 6) / 100.0; + if (callback) callback(DC_SAMPLE_CNS, sample, userdata); + } else if (event == EVENT_SETPOINT_MANUAL || event == EVENT_SETPOINT_AUTO) { + sample.setpoint = data[6] / 100.0; + if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata); + } + } else if (type == LREC_MEASURE) { + // Measurement record. + if (id == MEASURE_ID_AI_PRESSURE) { + for (unsigned int i = 0; i < NTANKS; ++i) { + unsigned int pressure = data[offset + 4 + i]; + if (pressure == 0 || pressure == 0xFF) + continue; + + unsigned int idx = divesoft_freedom_find_tank (parser->tank, parser->ntanks, i); + if (idx >= parser->ntanks) { + ERROR (abstract->context, "Tank %u not found.", idx); + return DC_STATUS_DATAFORMAT; + } + + sample.pressure.tank = idx; + sample.pressure.value = pressure * 2.0; + if (callback) callback(DC_SAMPLE_PRESSURE, sample, userdata); + } + } else if (id == MEASURE_ID_OXYGEN) { + for (unsigned int i = 0; i < NSENSORS; ++i) { + unsigned int ppo2 = array_uint16_le (data + offset + 4 + i * 2); + if (ppo2 == 0 || ppo2 == 0xFFFF) + continue; + sample.ppo2 = ppo2 * 10.0 / BAR; + if (callback) callback(DC_SAMPLE_PPO2, sample, userdata); + } + } else if (id == MEASURE_ID_OXYGEN_MV) { + for (unsigned int i = 0; i < NSENSORS; ++i) { + unsigned int value = array_uint16_le (data + offset + 4 + i * 2); + unsigned int state = data[offset + 12 + i]; + if (!parser->calibrated || state == SENSTAT_UNCALIBRATED || + state == SENSTAT_NOT_EXIST) + continue; + sample.ppo2 = value / 100.0 * parser->calibration[i] / BAR; + if (callback) callback(DC_SAMPLE_PPO2, sample, userdata); + } + } + } else if (type == LREC_STATE) { + // Tissue saturation record. + } + + offset += RECORD_SIZE; + } + + return DC_STATUS_SUCCESS; +} diff --git a/src/parser.c b/src/parser.c index 961f59e..63485d2 100644 --- a/src/parser.c +++ b/src/parser.c @@ -63,6 +63,7 @@ #include "seac_screen.h" #include "deepblu_cosmiq.h" #include "oceans_s1.h" +#include "divesoft_freedom.h" #include "context-private.h" #include "parser-private.h" @@ -199,6 +200,9 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa case DC_FAMILY_OCEANS_S1: rc = oceans_s1_parser_create (&parser, context); break; + case DC_FAMILY_DIVESOFT_FREEDOM: + rc = divesoft_freedom_parser_create (&parser, context); + break; default: return DC_STATUS_INVALIDARGS; } From 72ddd6a4392e38a31d4ed450adcf748ea6ff0052 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 18 Apr 2023 22:57:33 +0200 Subject: [PATCH 19/25] Fix a typo in the documentation --- doc/man/dc_datetime_gmtime.3 | 2 +- doc/man/dc_datetime_localtime.3 | 2 +- doc/man/dc_datetime_mktime.3 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/man/dc_datetime_gmtime.3 b/doc/man/dc_datetime_gmtime.3 index 1e4bcf5..9297446 100644 --- a/doc/man/dc_datetime_gmtime.3 +++ b/doc/man/dc_datetime_gmtime.3 @@ -82,7 +82,7 @@ is .Sh SEE ALSO .Xr dc_datetime_localtime 3 , .Xr dc_datetime_mktime 3 , -.Xr dc_datetime_new 3 +.Xr dc_datetime_now 3 .Sh AUTHORS The .Lb libdivecomputer diff --git a/doc/man/dc_datetime_localtime.3 b/doc/man/dc_datetime_localtime.3 index ec6681f..8b9c2f6 100644 --- a/doc/man/dc_datetime_localtime.3 +++ b/doc/man/dc_datetime_localtime.3 @@ -82,7 +82,7 @@ is .Sh SEE ALSO .Xr dc_datetime_gmtime 3 , .Xr dc_datetime_mktime 3 , -.Xr dc_datetime_new 3 +.Xr dc_datetime_now 3 .Sh AUTHORS The .Lb libdivecomputer diff --git a/doc/man/dc_datetime_mktime.3 b/doc/man/dc_datetime_mktime.3 index a3dbccb..4ba38b3 100644 --- a/doc/man/dc_datetime_mktime.3 +++ b/doc/man/dc_datetime_mktime.3 @@ -47,7 +47,7 @@ may not sanely be converted. .Sh SEE ALSO .Xr dc_datetime_gmtime 3 , .Xr dc_datetime_localtime 3 , -.Xr dc_datetime_new 3 +.Xr dc_datetime_now 3 .Sh AUTHORS The .Lb libdivecomputer From 40c95ca02a7456b004b3ce90c9d21814081f2847 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 15 Mar 2023 19:31:31 +0100 Subject: [PATCH 20/25] Add a generic packet I/O implementation The new packet I/O provides a layered I/O for reading and writing a byte stream from the underlying packet oriented transport. --- contrib/android/Android.mk | 1 + contrib/msvc/libdivecomputer.vcxproj | 2 + src/Makefile.am | 1 + src/packet.c | 321 +++++++++++++++++++++++++++ src/packet.h | 54 +++++ 5 files changed, 379 insertions(+) create mode 100644 src/packet.c create mode 100644 src/packet.h diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk index 6c61f26..fcceb81 100644 --- a/contrib/android/Android.mk +++ b/contrib/android/Android.mk @@ -69,6 +69,7 @@ LOCAL_SRC_FILES := \ src/oceans_s1.c \ src/oceans_s1_common.c \ src/oceans_s1_parser.c \ + src/packet.c \ src/parser.c \ src/platform.c \ src/rbstream.c \ diff --git a/contrib/msvc/libdivecomputer.vcxproj b/contrib/msvc/libdivecomputer.vcxproj index 8b3ceab..7f1335c 100644 --- a/contrib/msvc/libdivecomputer.vcxproj +++ b/contrib/msvc/libdivecomputer.vcxproj @@ -237,6 +237,7 @@ + @@ -355,6 +356,7 @@ + diff --git a/src/Makefile.am b/src/Makefile.am index ef44da3..de59dd6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -82,6 +82,7 @@ libdivecomputer_la_SOURCES = \ oceans_s1.h oceans_s1.c oceans_s1_parser.c \ divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \ hdlc.h hdlc.c \ + packet.h packet.c \ socket.h socket.c \ irda.c \ usb.c \ diff --git a/src/packet.c b/src/packet.c new file mode 100644 index 0000000..cc8304f --- /dev/null +++ b/src/packet.c @@ -0,0 +1,321 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jef Driesen + * + * 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 // malloc, free +#include + +#include "packet.h" + +#include "iostream-private.h" +#include "common-private.h" +#include "context-private.h" + +static dc_status_t dc_packet_set_timeout (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_packet_set_break (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_packet_set_dtr (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_packet_set_rts (dc_iostream_t *abstract, unsigned int value); +static dc_status_t dc_packet_get_lines (dc_iostream_t *abstract, unsigned int *value); +static dc_status_t dc_packet_get_available (dc_iostream_t *abstract, size_t *value); +static dc_status_t dc_packet_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol); +static dc_status_t dc_packet_poll (dc_iostream_t *abstract, int timeout); +static dc_status_t dc_packet_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual); +static dc_status_t dc_packet_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual); +static dc_status_t dc_packet_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size); +static dc_status_t dc_packet_flush (dc_iostream_t *abstract); +static dc_status_t dc_packet_purge (dc_iostream_t *abstract, dc_direction_t direction); +static dc_status_t dc_packet_sleep (dc_iostream_t *abstract, unsigned int milliseconds); +static dc_status_t dc_packet_close (dc_iostream_t *abstract); + +typedef struct dc_packet_t { + /* Base class. */ + dc_iostream_t base; + /* Internal state. */ + dc_iostream_t *iostream; + unsigned char *cache; + size_t available; + size_t offset; + size_t isize; + size_t osize; +} dc_packet_t; + +static const dc_iostream_vtable_t dc_packet_vtable = { + sizeof(dc_packet_t), + dc_packet_set_timeout, /* set_timeout */ + dc_packet_set_break, /* set_break */ + dc_packet_set_dtr, /* set_dtr */ + dc_packet_set_rts, /* set_rts */ + dc_packet_get_lines, /* get_lines */ + dc_packet_get_available, /* get_available */ + dc_packet_configure, /* configure */ + dc_packet_poll, /* poll */ + dc_packet_read, /* read */ + dc_packet_write, /* write */ + dc_packet_ioctl, /* ioctl */ + dc_packet_flush, /* flush */ + dc_packet_purge, /* purge */ + dc_packet_sleep, /* sleep */ + dc_packet_close, /* close */ +}; + +dc_status_t +dc_packet_open (dc_iostream_t **out, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_packet_t *packet = NULL; + unsigned char *buffer = NULL; + + if (out == NULL || base == NULL) + return DC_STATUS_INVALIDARGS; + + // Allocate memory. + packet = (dc_packet_t *) dc_iostream_allocate (NULL, &dc_packet_vtable, dc_iostream_get_transport(base)); + if (packet == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_exit; + } + + // Allocate the read buffer. + if (isize) { + buffer = (unsigned char *) malloc (isize); + if (buffer == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_free; + } + } + + packet->iostream = base; + packet->cache = buffer; + packet->available = 0; + packet->offset = 0; + packet->isize = isize; + packet->osize = osize; + + *out = (dc_iostream_t *) packet; + + return DC_STATUS_SUCCESS; + +error_free: + dc_iostream_deallocate ((dc_iostream_t *) packet); +error_exit: + return status; +} + +static dc_status_t +dc_packet_set_timeout (dc_iostream_t *abstract, int timeout) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_timeout (packet->iostream, timeout); +} + +static dc_status_t +dc_packet_set_break (dc_iostream_t *abstract, unsigned int value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_break (packet->iostream, value); +} + +static dc_status_t +dc_packet_set_dtr (dc_iostream_t *abstract, unsigned int value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_dtr (packet->iostream, value); +} + +static dc_status_t +dc_packet_set_rts (dc_iostream_t *abstract, unsigned int value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_set_rts (packet->iostream, value); +} + +static dc_status_t +dc_packet_get_lines (dc_iostream_t *abstract, unsigned int *value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_get_lines (packet->iostream, value); +} + +static dc_status_t +dc_packet_get_available (dc_iostream_t *abstract, size_t *value) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + if (packet->isize && packet->available) { + if (value) + *value = packet->available; + return DC_STATUS_SUCCESS; + } + + return dc_iostream_get_available (packet->iostream, value); +} + +static dc_status_t +dc_packet_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_configure (packet->iostream, baudrate, databits, parity, stopbits, flowcontrol); +} + +static dc_status_t +dc_packet_poll (dc_iostream_t *abstract, int timeout) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + if (packet->isize && packet->available) + return DC_STATUS_SUCCESS; + + return dc_iostream_poll (packet->iostream, timeout); +} + +static dc_status_t +dc_packet_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_packet_t *packet = (dc_packet_t *) abstract; + size_t nbytes = 0; + + while (nbytes < size) { + // Get the remaining size. + size_t length = size - nbytes; + + if (packet->isize) { + if (packet->available == 0) { + // Read a packet into the cache. + size_t len = 0; + status = dc_iostream_read (packet->iostream, packet->cache, packet->isize, &len); + if (status != DC_STATUS_SUCCESS) + break; + + packet->available = len; + packet->offset = 0; + } + + // Limit to the maximum packet size. + if (length > packet->available) + length = packet->available; + + // Copy the data from the cached packet. + memcpy ((unsigned char *) data + nbytes, packet->cache + packet->offset, length); + packet->available -= length; + packet->offset += length; + } else { + // Read the packet. + status = dc_iostream_read (packet->iostream, (unsigned char *) data + nbytes, length, &length); + if (status != DC_STATUS_SUCCESS) + break; + } + + // Update the total number of bytes. + nbytes += length; + } + + if (actual) + *actual = nbytes; + + return status; +} + +static dc_status_t +dc_packet_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_packet_t *packet = (dc_packet_t *) abstract; + size_t nbytes = 0; + + while (nbytes < size) { + // Get the remaining size. + size_t length = size - nbytes; + + // Limit to the maximum packet size. + if (packet->osize) { + if (length > packet->osize) + length = packet->osize; + } + + // Write the packet. + status = dc_iostream_write (packet->iostream, (const unsigned char *) data + nbytes, length, &length); + if (status != DC_STATUS_SUCCESS) + break; + + // Update the total number of bytes. + nbytes += length; + } + + if (actual) + *actual = nbytes; + + return status; +} + +static dc_status_t +dc_packet_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_ioctl (packet->iostream, request, data, size); +} + +static dc_status_t +dc_packet_flush (dc_iostream_t *abstract) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_flush (packet->iostream); +} + +static dc_status_t +dc_packet_purge (dc_iostream_t *abstract, dc_direction_t direction) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + if (direction & DC_DIRECTION_INPUT) { + packet->available = 0; + packet->offset = 0; + } + + return dc_iostream_purge (packet->iostream, direction); +} + +static dc_status_t +dc_packet_sleep (dc_iostream_t *abstract, unsigned int milliseconds) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + return dc_iostream_sleep (packet->iostream, milliseconds); +} + +static dc_status_t +dc_packet_close (dc_iostream_t *abstract) +{ + dc_packet_t *packet = (dc_packet_t *) abstract; + + free (packet->cache); + + return DC_STATUS_SUCCESS; +} diff --git a/src/packet.h b/src/packet.h new file mode 100644 index 0000000..b420f08 --- /dev/null +++ b/src/packet.h @@ -0,0 +1,54 @@ +/* + * libdivecomputer + * + * Copyright (C) 2023 Jef Driesen + * + * 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 DC_PACKET_H +#define DC_PACKET_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Create a packet I/O stream layered on top of another base I/O stream. + * + * This layered I/O allows reading and writing a byte stream from the + * underlying packet oriented transport. It changes the packet oriented + * base transport into a stream oriented transport. + * + * @param[out] iostream A location to store the packet I/O stream. + * @param[in] context A valid context. + * @param[in] base A valid I/O stream. + * @param[in] isize The input packet size in bytes. + * @param[in] osize The output packet size in bytes. + * @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code + * on failure. + */ +dc_status_t +dc_packet_open (dc_iostream_t **iostream, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* DC_PACKET_H */ From 1c8cd096b57a876c4fb0afc5113aac05d75d924e Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 13 Apr 2023 19:38:42 +0200 Subject: [PATCH 21/25] Integrate the new packet I/O in the backends Replace the custom packet handling code in the iconhd and ostc3 backends with the new layered packet I/O, and also integrate it into the idive and extreme backends. --- src/divesystem_idive.c | 37 +++++++++-- src/hw_ostc3.c | 83 ++++++++++++------------- src/mares_iconhd.c | 136 +++++++++++++---------------------------- src/mclean_extreme.c | 40 +++++++++--- 4 files changed, 150 insertions(+), 146 deletions(-) diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c index 4a0bca3..9841a6f 100644 --- a/src/divesystem_idive.c +++ b/src/divesystem_idive.c @@ -29,6 +29,7 @@ #include "platform.h" #include "checksum.h" #include "array.h" +#include "packet.h" #define ISINSTANCE(device) dc_device_isinstance((device), &divesystem_idive_device_vtable) @@ -102,6 +103,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 dc_status_t divesystem_idive_device_close (dc_device_t *abstract); static const dc_device_vtable_t divesystem_idive_device_vtable = { sizeof(divesystem_idive_device_t), @@ -112,7 +114,7 @@ static const dc_device_vtable_t divesystem_idive_device_vtable = { NULL, /* dump */ divesystem_idive_device_foreach, /* foreach */ divesystem_idive_device_timesync, /* timesync */ - NULL /* close */ + divesystem_idive_device_close /* close */ }; static const divesystem_idive_commands_t idive = { @@ -152,6 +154,7 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr { dc_status_t status = DC_STATUS_SUCCESS; divesystem_idive_device_t *device = NULL; + dc_transport_t transport = dc_iostream_get_transport (iostream); if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -164,22 +167,32 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr } // Set the default values. - device->iostream = iostream; memset (device->fingerprint, 0, sizeof (device->fingerprint)); device->model = model; + // Create the packet stream. + if (transport == DC_TRANSPORT_BLE) { + status = dc_packet_open (&device->iostream, context, iostream, 244, 244); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the packet stream."); + goto error_free; + } + } else { + device->iostream = iostream; + } + // Set the serial communication protocol (115200 8N1). status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the terminal attributes."); - goto error_free; + goto error_free_iostream; } // Set the timeout for receiving data (1000ms). status = dc_iostream_set_timeout (device->iostream, 1000); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the timeout."); - goto error_free; + goto error_free_iostream; } // Make sure everything is in a sane state. @@ -190,11 +203,27 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr return DC_STATUS_SUCCESS; +error_free_iostream: + if (transport == DC_TRANSPORT_BLE) { + dc_iostream_close (device->iostream); + } error_free: dc_device_deallocate ((dc_device_t *) device); return status; } +static dc_status_t +divesystem_idive_device_close (dc_device_t *abstract) +{ + divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract; + + // Close the packet stream. + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + return dc_iostream_close (device->iostream); + } + + return DC_STATUS_SUCCESS; +} static dc_status_t divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 629e482..2016158 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -30,6 +30,7 @@ #include "array.h" #include "aes.h" #include "platform.h" +#include "packet.h" #define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable) @@ -119,9 +120,6 @@ typedef struct hw_ostc3_device_t { unsigned int firmware; unsigned char fingerprint[5]; hw_ostc3_state_t state; - unsigned char cache[244]; - unsigned int available; - unsigned int offset; } hw_ostc3_device_t; typedef struct hw_ostc3_logbook_t { @@ -207,41 +205,20 @@ static dc_status_t hw_ostc3_read (hw_ostc3_device_t *device, dc_event_progress_t *progress, unsigned char data[], size_t size) { dc_status_t rc = DC_STATUS_SUCCESS; - dc_transport_t transport = dc_iostream_get_transport(device->iostream); size_t nbytes = 0; while (nbytes < size) { - if (transport == DC_TRANSPORT_BLE) { - if (device->available == 0) { - // Read a packet into the cache. - size_t len = 0; - rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len); - if (rc != DC_STATUS_SUCCESS) - return rc; - - device->available = len; - device->offset = 0; - } - } - // Set the minimum packet size. - size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : 1024; + size_t length = 1024; // Limit the packet size to the total size. if (nbytes + length > size) length = size - nbytes; - if (transport == DC_TRANSPORT_BLE) { - // Copy the data from the cached packet. - memcpy (data + nbytes, device->cache + device->offset, length); - device->available -= length; - device->offset += length; - } else { - // Read the packet. - rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL); - if (rc != DC_STATUS_SUCCESS) - return rc; - } + // Read the packet. + rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL); + if (rc != DC_STATUS_SUCCESS) + return rc; // Update and emit a progress event. if (progress) { @@ -259,12 +236,11 @@ static dc_status_t hw_ostc3_write (hw_ostc3_device_t *device, dc_event_progress_t *progress, const unsigned char data[], size_t size) { dc_status_t rc = DC_STATUS_SUCCESS; - dc_transport_t transport = dc_iostream_get_transport(device->iostream); size_t nbytes = 0; while (nbytes < size) { // Set the maximum packet size. - size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : 64; + size_t length = 1024; // Limit the packet size to the total size. if (nbytes + length > size) @@ -313,7 +289,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, // Send the command. unsigned char command[1] = {cmd}; - status = hw_ostc3_write (device, NULL, command, sizeof (command)); + status = dc_iostream_write (device->iostream, command, sizeof (command), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; @@ -321,7 +297,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, // Read the echo. unsigned char echo[1] = {0}; - status = hw_ostc3_read (device, NULL, echo, sizeof (echo)); + status = dc_iostream_read (device->iostream, echo, sizeof (echo), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the echo."); return status; @@ -407,14 +383,14 @@ hw_ostc3_transfer (hw_ostc3_device_t *device, } } - if (delay && device->available == 0) { + if (delay) { dc_iostream_poll (device->iostream, delay); } if (cmd != EXIT) { // Read the ready byte. unsigned char answer[1] = {0}; - status = hw_ostc3_read (device, NULL, answer, sizeof (answer)); + status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the ready byte."); return status; @@ -439,6 +415,7 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i { dc_status_t status = DC_STATUS_SUCCESS; hw_ostc3_device_t *device = NULL; + dc_transport_t transport = dc_iostream_get_transport (iostream); if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -451,29 +428,36 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i } // Set the default values. - device->iostream = iostream; device->hardware = INVALID; device->feature = 0; device->model = 0; device->serial = 0; device->firmware = 0; memset (device->fingerprint, 0, sizeof (device->fingerprint)); - memset (device->cache, 0, sizeof (device->cache)); - device->available = 0; - device->offset = 0; + + // Create the packet stream. + if (transport == DC_TRANSPORT_BLE) { + status = dc_packet_open (&device->iostream, context, iostream, 244, 244); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the packet stream."); + goto error_free; + } + } else { + device->iostream = iostream; + } // Set the serial communication protocol (115200 8N1). status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the terminal attributes."); - goto error_free; + goto error_free_iostream; } // Set the timeout for receiving data (3000ms). status = dc_iostream_set_timeout (device->iostream, 3000); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the timeout."); - goto error_free; + goto error_free_iostream; } // Make sure everything is in a sane state. @@ -486,6 +470,10 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i return DC_STATUS_SUCCESS; +error_free_iostream: + if (transport == DC_TRANSPORT_BLE) { + dc_iostream_close (device->iostream); + } error_free: dc_device_deallocate ((dc_device_t *) device); return status; @@ -548,14 +536,14 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device) unsigned char answer[5] = {0}; // Send the command and service key. - status = hw_ostc3_write (device, NULL, command, sizeof (command)); + status = dc_iostream_write (device->iostream, command, sizeof (command), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; } // Read the response. - status = hw_ostc3_read (device, NULL, answer, sizeof (answer)); + status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; @@ -656,6 +644,15 @@ hw_ostc3_device_close (dc_device_t *abstract) } } + // Close the packet stream. + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + rc = dc_iostream_close (device->iostream); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to close the packet stream."); + dc_status_set_error(&status, rc); + } + } + return status; } diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index 55b684f..2f5bd28 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -29,6 +29,7 @@ #include "array.h" #include "rbstream.h" #include "platform.h" +#include "packet.h" #define ISINSTANCE(device) dc_device_isinstance((device), &mares_iconhd_device_vtable) @@ -93,15 +94,13 @@ typedef struct mares_iconhd_device_t { unsigned char version[140]; unsigned int model; unsigned int packetsize; - unsigned char cache[20]; - unsigned int available; - unsigned int offset; } mares_iconhd_device_t; static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static dc_status_t mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); +static dc_status_t mares_iconhd_device_close (dc_device_t *abstract); static const dc_device_vtable_t mares_iconhd_device_vtable = { sizeof(mares_iconhd_device_t), @@ -112,7 +111,7 @@ static const dc_device_vtable_t mares_iconhd_device_vtable = { mares_iconhd_device_dump, /* dump */ mares_iconhd_device_foreach, /* foreach */ NULL, /* timesync */ - NULL /* close */ + mares_iconhd_device_close /* close */ }; static const mares_iconhd_layout_t mares_iconhd_layout = { @@ -177,78 +176,6 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) return model; } -static dc_status_t -mares_iconhd_read (mares_iconhd_device_t *device, unsigned char data[], size_t size) -{ - dc_status_t rc = DC_STATUS_SUCCESS; - dc_transport_t transport = dc_iostream_get_transport(device->iostream); - - size_t nbytes = 0; - while (nbytes < size) { - if (transport == DC_TRANSPORT_BLE) { - if (device->available == 0) { - // Read a packet into the cache. - size_t len = 0; - rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len); - if (rc != DC_STATUS_SUCCESS) - return rc; - - device->available = len; - device->offset = 0; - } - } - - // Set the minimum packet size. - size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : size - nbytes; - - // Limit the packet size to the total size. - if (nbytes + length > size) - length = size - nbytes; - - if (transport == DC_TRANSPORT_BLE) { - // Copy the data from the cached packet. - memcpy (data + nbytes, device->cache + device->offset, length); - device->available -= length; - device->offset += length; - } else { - // Read the packet. - rc = dc_iostream_read (device->iostream, data + nbytes, length, &length); - if (rc != DC_STATUS_SUCCESS) - return rc; - } - - nbytes += length; - } - - return rc; -} - -static dc_status_t -mares_iconhd_write (mares_iconhd_device_t *device, const unsigned char data[], size_t size) -{ - dc_status_t rc = DC_STATUS_SUCCESS; - dc_transport_t transport = dc_iostream_get_transport(device->iostream); - - size_t nbytes = 0; - while (nbytes < size) { - // Set the maximum packet size. - size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : size - nbytes; - - // Limit the packet size to the total size. - if (nbytes + length > size) - length = size - nbytes; - - // Write the packet. - rc = dc_iostream_write (device->iostream, data + nbytes, length, &length); - if (rc != DC_STATUS_SUCCESS) - return rc; - - nbytes += length; - } - - return rc; -} - static dc_status_t mares_iconhd_packet (mares_iconhd_device_t *device, const unsigned char command[], unsigned int csize, @@ -263,7 +190,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, return DC_STATUS_CANCELLED; // Send the command header to the dive computer. - status = mares_iconhd_write (device, command, 2); + status = dc_iostream_write (device->iostream, command, 2, NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; @@ -271,7 +198,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, // Receive the header byte. unsigned char header[1] = {0}; - status = mares_iconhd_read (device, header, sizeof (header)); + status = dc_iostream_read (device->iostream, header, sizeof (header), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; @@ -285,7 +212,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, // Send the command payload to the dive computer. if (csize > 2) { - status = mares_iconhd_write (device, command + 2, csize - 2); + status = dc_iostream_write (device->iostream, command + 2, csize - 2, NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to send the command."); return status; @@ -293,7 +220,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, } // Read the packet. - status = mares_iconhd_read (device, answer, asize); + status = dc_iostream_read (device->iostream, answer, asize, NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; @@ -301,7 +228,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device, // Receive the trailer byte. unsigned char trailer[1] = {0}; - status = mares_iconhd_read (device, trailer, sizeof (trailer)); + status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to receive the answer."); return status; @@ -334,8 +261,6 @@ mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char comman // Discard any garbage bytes. dc_iostream_sleep (device->iostream, 100); dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT); - device->available = 0; - device->offset = 0; } return DC_STATUS_SUCCESS; @@ -459,6 +384,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ { dc_status_t status = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = NULL; + dc_transport_t transport = dc_iostream_get_transport (iostream); if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -471,43 +397,50 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ } // Set the default values. - device->iostream = iostream; device->layout = NULL; memset (device->fingerprint, 0, sizeof (device->fingerprint)); device->fingerprint_size = sizeof (device->fingerprint); memset (device->version, 0, sizeof (device->version)); device->model = 0; device->packetsize = 0; - memset (device->cache, 0, sizeof (device->cache)); - device->available = 0; - device->offset = 0; + + // Create the packet stream. + if (transport == DC_TRANSPORT_BLE) { + status = dc_packet_open (&device->iostream, context, iostream, 20, 20); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the packet stream."); + goto error_free; + } + } else { + device->iostream = iostream; + } // Set the serial communication protocol (115200 8E1). status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to set the terminal attributes."); - goto error_free; + goto error_free_iostream; } // 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; + goto error_free_iostream; } // Clear the DTR line. status = dc_iostream_set_dtr (device->iostream, 0); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to clear the DTR line."); - goto error_free; + goto error_free_iostream; } // Clear the RTS line. status = dc_iostream_set_rts (device->iostream, 0); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to clear the RTS line."); - goto error_free; + goto error_free_iostream; } // Make sure everything is in a sane state. @@ -518,7 +451,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ status = mares_iconhd_transfer (device, command, sizeof (command), device->version, sizeof (device->version)); if (status != DC_STATUS_SUCCESS) { - goto error_free; + goto error_free_iostream; } // Autodetect the model using the version packet. @@ -586,12 +519,31 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ return DC_STATUS_SUCCESS; + +error_free_iostream: + if (transport == DC_TRANSPORT_BLE) { + dc_iostream_close (device->iostream); + } error_free: dc_device_deallocate ((dc_device_t *) device); return status; } +static dc_status_t +mares_iconhd_device_close (dc_device_t *abstract) +{ + mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; + + // Close the packet stream. + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + return dc_iostream_close (device->iostream); + } + + return DC_STATUS_SUCCESS; +} + + static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) { diff --git a/src/mclean_extreme.c b/src/mclean_extreme.c index 98038ff..2ef1a87 100644 --- a/src/mclean_extreme.c +++ b/src/mclean_extreme.c @@ -26,6 +26,7 @@ #include "context-private.h" #include "device-private.h" #include "array.h" +#include "packet.h" #define ISINSTANCE(device) dc_device_isinstance((device), &mclean_extreme_device_vtable) @@ -391,6 +392,7 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream { dc_status_t status = DC_STATUS_SUCCESS; mclean_extreme_device_t *device = NULL; + dc_transport_t transport = dc_iostream_get_transport (iostream); if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -403,21 +405,31 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream } // Set the default values. - device->iostream = iostream; memset(device->fingerprint, 0, sizeof(device->fingerprint)); + // Create the packet stream. + if (transport == DC_TRANSPORT_BLE) { + status = dc_packet_open (&device->iostream, context, iostream, 244, 244); + if (status != DC_STATUS_SUCCESS) { + ERROR (context, "Failed to create the packet stream."); + goto error_free; + } + } else { + device->iostream = iostream; + } + // Set the serial communication protocol (115200 8N1). status = dc_iostream_configure(device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); if (status != DC_STATUS_SUCCESS) { ERROR(context, "Failed to set the terminal attributes."); - goto error_free; + goto error_free_iostream; } // Set the timeout for receiving data (1000ms). status = dc_iostream_set_timeout(device->iostream, 1000); if (status != DC_STATUS_SUCCESS) { ERROR(context, "Failed to set the timeout."); - goto error_free; + goto error_free_iostream; } // Make sure everything is in a sane state. @@ -428,6 +440,10 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream return DC_STATUS_SUCCESS; +error_free_iostream: + if (transport == DC_TRANSPORT_BLE) { + dc_iostream_close (device->iostream); + } error_free: dc_device_deallocate((dc_device_t *)device); return status; @@ -438,14 +454,24 @@ mclean_extreme_device_close(dc_device_t *abstract) { dc_status_t status = DC_STATUS_SUCCESS; mclean_extreme_device_t *device = (mclean_extreme_device_t *)abstract; + dc_status_t rc = DC_STATUS_SUCCESS; - status = mclean_extreme_send(device, CMD_CLOSE, NULL, 0); - if (status != DC_STATUS_SUCCESS) { + rc = mclean_extreme_send(device, CMD_CLOSE, NULL, 0); + if (rc != DC_STATUS_SUCCESS) { ERROR(abstract->context, "Failed to send the exit command."); - return status; + dc_status_set_error(&status, rc); } - return DC_STATUS_SUCCESS; + // Close the packet stream. + if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + rc = dc_iostream_close (device->iostream); + if (rc != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to close the packet stream."); + dc_status_set_error(&status, rc); + } + } + + return status; } static dc_status_t From 6c3bbb2cc74f9628a7754f4412e23b08f5a3464f Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Fri, 5 May 2023 19:47:52 +0200 Subject: [PATCH 22/25] Reduce the BLE output packet size to 20 bytes again The newer u-Blox Nina B2 bluetooth module supports larger packets up to 244 bytes, but the older Telit/Stollman bluetooth module does not. Trying to send a packet larger than 20 bytes fails. For maximum compatibility, limit the output packet size to 20 bytes. --- src/hw_ostc3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c index 2016158..f4bac54 100644 --- a/src/hw_ostc3.c +++ b/src/hw_ostc3.c @@ -437,7 +437,7 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i // Create the packet stream. if (transport == DC_TRANSPORT_BLE) { - status = dc_packet_open (&device->iostream, context, iostream, 244, 244); + 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 2f1b99f2f97a2161fc4e7b17e00a364f84a2614d Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 9 May 2023 22:19:12 +0200 Subject: [PATCH 23/25] Add a missing filter for the Aqualung i750TC Commit 58d410b1a217bbc1f03d5c7a5a203c139a92616c accidentally omitted the descriptor filter function. --- src/descriptor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/descriptor.c b/src/descriptor.c index 9858da6..c62baa3 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -256,7 +256,7 @@ static const dc_descriptor_t g_descriptors[] = { {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, DC_TRANSPORT_SERIAL, NULL}, {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559, DC_TRANSPORT_SERIAL, NULL}, - {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, NULL}, + {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL}, From 49aa12b172007c46294655c0c2388fb5f3562bc7 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Wed, 10 May 2023 23:02:05 +0200 Subject: [PATCH 24/25] Fix the date of the v0.7.0 release --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 3fabd4d..3996359 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Version 0.7.0 (2020-05-07) +Version 0.7.0 (2021-05-07) ========================== The main highlight of the v0.7.0 release is the introduction of the new From db9371cf9f38b5a3b2f6e4fae3f92eb052d2d929 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 11 May 2023 18:56:37 +0200 Subject: [PATCH 25/25] Release version 0.8.0 --- NEWS | 34 ++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 3996359..df264ae 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,37 @@ +Version 0.8.0 (2023-05-11) +========================== + +The v0.8.0 release is mainly a bugfix release, and brings in support for a +number of new devices. This release is fully backwards compatible with the +previous one. + +New features: + + * Add support for new backends: + - excursion: Deep Six Excursion, Crest CR-4, Genesis Centauri, Tusa TC1, Scorpena Alpha + - screen: Seac Screen and Action + - cosmiq: Deepblu Cosmiq+ + - s1: Oceans S1 + - freedom: Divesoft Freedom and Liberty + * Add support for some new devices: + - Aqualung: i200C + - Cressi: Donatello, Michelangelo, Neon + - Mares: Puck Pro + + - Oceanic: Geo Air + - Ratio: iX3M 2 + - Scubapro: G2 TEK + - Shearwater: Petrel 3, Perdix 2 + - Sherwood: Amphos Air 2.0 + * Add support for parsing the decompression model + * Add a public api to configure the depth calibration + * Add a public api to configure the clock synchronization + * Add a basic Android build system + +Removed/changed features: + + * Migrate to Visual Studio 2013 (or newer) + * Move the Visual Studio project to the contrib directory + Version 0.7.0 (2021-05-07) ========================== diff --git a/configure.ac b/configure.ac index e46f8f1..062b566 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ m4_define([dc_version_major],[0]) m4_define([dc_version_minor],[8]) m4_define([dc_version_micro],[0]) -m4_define([dc_version_suffix],[devel]) +m4_define([dc_version_suffix],[]) m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix])) # Libtool versioning.