diff --git a/src/descriptor.c b/src/descriptor.c index 096c8ee..83fd3a9 100644 --- a/src/descriptor.c +++ b/src/descriptor.c @@ -307,6 +307,10 @@ static const dc_descriptor_t g_descriptors[] = { {"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Horizon", DC_FAMILY_MARES_ICONHD , 0x2C, DC_TRANSPORT_SERIAL, NULL}, + {"Mares", "Puck Air 2", DC_FAMILY_MARES_ICONHD , 0x2D, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Sirius", DC_FAMILY_MARES_ICONHD , 0x2F, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Quad Ci", DC_FAMILY_MARES_ICONHD , 0x31, DC_TRANSPORT_BLE, dc_filter_mares}, + {"Mares", "Puck 4", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares}, /* Heinrichs Weikamp */ {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL}, @@ -706,6 +710,9 @@ dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const vo static const char * const bluetooth[] = { "Mares bluelink pro", "Mares Genius", + "Sirius", + "Quad Ci", + "Puck4", }; if (transport == DC_TRANSPORT_BLE) { diff --git a/src/device.c b/src/device.c index ff6a198..a7e60b7 100644 --- a/src/device.c +++ b/src/device.c @@ -176,7 +176,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr rc = mares_darwin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_MARES_ICONHD: - rc = mares_iconhd_device_open (&device, context, iostream); + rc = mares_iconhd_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); break; case DC_FAMILY_HW_OSTC: rc = hw_ostc_device_open (&device, context, iostream); diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c index db6d20b..c968685 100644 --- a/src/mares_iconhd.c +++ b/src/mares_iconhd.c @@ -48,6 +48,10 @@ #define SMARTAIR 0x24 #define QUAD 0x29 #define HORIZON 0x2C +#define PUCKAIR2 0x2D +#define SIRIUS 0x2F +#define QUADCI 0x31 +#define PUCK4 0x35 #define ISSMART(model) ( \ (model) == SMART || \ @@ -56,10 +60,25 @@ #define ISGENIUS(model) ( \ (model) == GENIUS || \ - (model) == HORIZON) + (model) == HORIZON || \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) + +#define ISSIRIUS(model) ( \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) #define MAXRETRIES 4 +#define MAXPACKET 244 + +#define FIXED 0 +#define VARIABLE 1 + #define ACK 0xAA #define END 0xEA #define XOR 0xA5 @@ -104,6 +123,7 @@ typedef struct mares_iconhd_device_t { unsigned char version[140]; unsigned int model; unsigned int packetsize; + unsigned int ble; } mares_iconhd_device_t; static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); @@ -171,6 +191,10 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) {"Smart Air", SMARTAIR}, {"Quad", QUAD}, {"Horizon", HORIZON}, + {"Puck Air 2", PUCKAIR2}, + {"Sirius", SIRIUS}, + {"Quad Ci", QUADCI}, + {"Puck4", PUCK4}, }; // Check the product name in the version packet against the list @@ -187,10 +211,11 @@ mares_iconhd_get_model (mares_iconhd_device_t *device) } static dc_status_t -mares_iconhd_packet (mares_iconhd_device_t *device, +mares_iconhd_packet_fixed (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, - unsigned char answer[], unsigned int asize) + unsigned char answer[], unsigned int asize, + unsigned int *actual) { dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; @@ -262,15 +287,120 @@ mares_iconhd_packet (mares_iconhd_device_t *device, return DC_STATUS_PROTOCOL; } + if (actual) { + *actual = asize; + } + return DC_STATUS_SUCCESS; } static dc_status_t -mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize) +mares_iconhd_packet_variable (mares_iconhd_device_t *device, + unsigned char cmd, + const unsigned char data[], unsigned int size, + unsigned char answer[], unsigned int asize, + unsigned int *actual) +{ + dc_status_t status = DC_STATUS_SUCCESS; + dc_device_t *abstract = (dc_device_t *) device; + unsigned char packet[MAXPACKET] = {0}; + size_t length = 0; + + if (device_is_cancelled (abstract)) + return DC_STATUS_CANCELLED; + + // Send the command header to the dive computer. + const unsigned char command[2] = { + cmd, cmd ^ XOR, + }; + status = dc_iostream_write (device->iostream, command, sizeof(command), NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command header."); + return status; + } + + // Read either the entire data packet (if there is no command data to send), + // or only the header byte (if there is also command data to send). + status = dc_iostream_read (device->iostream, packet, sizeof (packet), &length); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet header."); + return status; + } + + if (size) { + // Send the command payload to the dive computer. + status = dc_iostream_write (device->iostream, data, size, NULL); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to send the command data."); + return status; + } + + // Read the data packet. + size_t len = 0; + status = dc_iostream_read (device->iostream, packet + length, sizeof (packet) - length, &len); + if (status != DC_STATUS_SUCCESS) { + ERROR (abstract->context, "Failed to receive the packet data."); + return status; + } + + length += len; + } + + if (length < 2 || length - 2 > asize) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length); + return DC_STATUS_PROTOCOL; + } + + // Verify the header byte. + if (packet[0] != ACK) { + ERROR (abstract->context, "Unexpected packet header byte (%02x).", packet[0]); + return DC_STATUS_PROTOCOL; + } + + // Verify the trailer byte. + if (packet[length - 1] != END) { + ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", packet[length - 1]); + return DC_STATUS_PROTOCOL; + } + + if (actual == NULL) { + // Verify the actual length. + if (length - 2 != asize) { + ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length - 2); + return DC_STATUS_PROTOCOL; + } + } else { + // Return the actual length. + *actual = length - 2; + } + + memcpy (answer, packet + 1, length - 2); + + return DC_STATUS_SUCCESS; +} + +static dc_status_t +mares_iconhd_packet (mares_iconhd_device_t *device, + unsigned char cmd, + const unsigned char data[], unsigned int size, + unsigned char answer[], unsigned int asize, + unsigned int *actual) +{ + dc_transport_t transport = dc_iostream_get_transport (device->iostream); + + if (transport == DC_TRANSPORT_BLE && device->ble == VARIABLE) { + return mares_iconhd_packet_variable (device, cmd, data, size, answer, asize, actual); + } else { + return mares_iconhd_packet_fixed (device, cmd, data, size, answer, asize, actual); + } +} + +static dc_status_t +mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *actual) { unsigned int nretries = 0; dc_status_t rc = DC_STATUS_SUCCESS; - while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize)) != DC_STATUS_SUCCESS) { + while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize, actual)) != DC_STATUS_SUCCESS) { // Automatically discard a corrupted packet, // and request a new one. if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) @@ -294,7 +424,9 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro dc_status_t status = DC_STATUS_SUCCESS; dc_device_t *abstract = (dc_device_t *) device; dc_transport_t transport = dc_iostream_get_transport (device->iostream); - const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? 124 : 504; + const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? + (device->ble == VARIABLE ? MAXPACKET - 3 : 124) : + 504; // Update and emit a progress event. unsigned int initial = 0; @@ -312,7 +444,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro subindex & 0xFF }; memset (cmd_init + 6, 0x00, sizeof(cmd_init) - 6); - status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init)); + status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init), NULL); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to transfer the init packet."); return status; @@ -366,13 +498,21 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } // Transfer the segment packet. + unsigned int length = 0; unsigned char rsp_segment[1 + 504]; - status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1); + status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1, &length); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to transfer the segment packet."); return status; } + if (length < 1) { + ERROR (abstract->context, "Unexpected packet length (%u).", length); + return DC_STATUS_PROTOCOL; + } + + length--; + // Verify the packet header. if ((rsp_segment[0] & 0xF0) >> 4 != toggle) { ERROR (abstract->context, "Unexpected packet header (%02x).", rsp_segment[0]); @@ -380,12 +520,12 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } // Append the payload to the output buffer. - if (!dc_buffer_append (buffer, rsp_segment + 1, len)) { + if (!dc_buffer_append (buffer, rsp_segment + 1, length)) { ERROR (abstract->context, "Insufficient buffer space available."); return DC_STATUS_NOMEMORY; } - nbytes += len; + nbytes += length; npackets++; // Update and emit the progress events. @@ -399,7 +539,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro } dc_status_t -mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream) +mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model) { dc_status_t status = DC_STATUS_SUCCESS; mares_iconhd_device_t *device = NULL; @@ -422,9 +562,10 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ memset (device->version, 0, sizeof (device->version)); device->model = 0; device->packetsize = 0; + device->ble = ISSIRIUS(model) ? VARIABLE : FIXED; // Create the packet stream. - if (transport == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { status = dc_packet_open (&device->iostream, context, iostream, 244, 20); if (status != DC_STATUS_SUCCESS) { ERROR (context, "Failed to create the packet stream."); @@ -467,7 +608,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ // Send the version command. status = mares_iconhd_transfer (device, CMD_VERSION, NULL, 0, - device->version, sizeof (device->version)); + device->version, sizeof (device->version), NULL); if (status != DC_STATUS_SUCCESS) { goto error_free_iostream; } @@ -479,7 +620,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ unsigned int memsize = 0; if (device->model == QUAD) { unsigned char rsp_flash[4] = {0}; - status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash)); + status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash), NULL); if (status != DC_STATUS_SUCCESS) { WARNING (context, "Failed to read the flash memory size."); } else { @@ -538,7 +679,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_ error_free_iostream: - if (transport == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { dc_iostream_close (device->iostream); } error_free: @@ -551,9 +692,10 @@ static dc_status_t mares_iconhd_device_close (dc_device_t *abstract) { mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract; + dc_transport_t transport = dc_iostream_get_transport (device->iostream); // Close the packet stream. - if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) { + if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) { return dc_iostream_close (device->iostream); } @@ -601,7 +743,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned (len >> 8) & 0xFF, (len >> 16) & 0xFF, (len >> 24) & 0xFF}; - rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len); + rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len, NULL); if (rc != DC_STATUS_SUCCESS) return rc; diff --git a/src/mares_iconhd.h b/src/mares_iconhd.h index 1b00c54..a6cb6a8 100644 --- a/src/mares_iconhd.h +++ b/src/mares_iconhd.h @@ -32,7 +32,7 @@ extern "C" { #endif /* __cplusplus */ dc_status_t -mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); +mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); dc_status_t mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model); diff --git a/src/mares_iconhd_parser.c b/src/mares_iconhd_parser.c index 223d7f8..855008a 100644 --- a/src/mares_iconhd_parser.c +++ b/src/mares_iconhd_parser.c @@ -45,6 +45,10 @@ #define QUADAIR 0x23 #define SMARTAIR 0x24 #define HORIZON 0x2C +#define PUCKAIR2 0x2D +#define SIRIUS 0x2F +#define QUADCI 0x31 +#define PUCK4 0x35 #define ISSMART(model) ( \ (model) == SMART || \ @@ -53,7 +57,11 @@ #define ISGENIUS(model) ( \ (model) == GENIUS || \ - (model) == HORIZON) + (model) == HORIZON || \ + (model) == PUCKAIR2 || \ + (model) == SIRIUS || \ + (model) == QUADCI || \ + (model) == PUCK4) #define NGASMIXES_ICONHD 3 #define NGASMIXES_GENIUS 5