Add support for the Mares Sirius (and compatible models)
The Mares Sirius uses the same communication protocol as the Genius, except for the fact that it uses a newer BLE version which supports larger data packets. The actual MTU is likely negotiated because we see different sizes like 244 and 182 bytes. We don't have access to this MTU size because not all BLE implementations can expose this information. Unfortunately not only the BLE packet size is variable, but also the size of the higher level data frames (used for downloading the content of the objects) is no longer fixed. The frame size appears to adapt to the BLE MTU size. This is most likely done to reduce the overhead and maximize the throughput. Although each frame ends with an OxEA byte, we can't rely on this knowledge to detect the end of the frame. The END byte is not escaped in the payload, and thus can also appear anywhere in the frame. As a workaround, we rely on the fact that each frame appears to be send as a single BLE packet. The only exception is the ACK byte, which gets send as a separate BLE packet if the command requires parameter data. Compatible models: Quad Ci, Puck 4 and Puck Air 2
This commit is contained in:
parent
f20d9cb972
commit
1663997111
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user