Add support for the Mares Bluelink Pro interface
The main difference with the serial communication is that the BLE communication uses data packets (with a maximum size of 20 bytes) instead of a continuous data stream. Occasionally, the device responds with an empty packet (with just the ACK and EOF byte) or with a short packet where one or more payload bytes are missing. The empty packet is most likely caused because the device didn't receive the second part of the command (with the parameters) in time. The missing bytes might be caused by a buffer overflow on the Bluelink Pro, when the data from the dive computer arrives faster over the serial link than the device can forward it over the slower bluetooth link to the host system. One way to address this problem is to lower the packet size from 256 bytes to only 128 bytes. The main disadvantage is that this also impacts the download speed considerably. In one particular example, the total download time increased from about 135 to 210 seconds (which is already slow compared to the 62 seconds the same download takes over usb)! An alternative solution is to simply retry the failed command. That way there is only a performance penalty for the few bad packets, and not for every single packet. In the above example, only two packets needed a retransmission. Note that the iconhd communication protocol uses no checksums. Hence it's not possible to detect corrupt data packets. Only short packets can be detected, because they result in a timeout.
This commit is contained in:
parent
88a814a616
commit
019a98f80c
@ -34,6 +34,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata);
|
|||||||
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
|
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata);
|
||||||
static int dc_filter_hw (dc_transport_t transport, const void *userdata);
|
static int dc_filter_hw (dc_transport_t transport, const void *userdata);
|
||||||
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
|
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata);
|
||||||
|
static int dc_filter_mares (dc_transport_t transport, const void *userdata);
|
||||||
|
|
||||||
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
|
||||||
|
|
||||||
@ -241,16 +242,16 @@ static const dc_descriptor_t g_descriptors[] = {
|
|||||||
{"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Airlab", DC_FAMILY_MARES_DARWIN , 1, DC_TRANSPORT_SERIAL, NULL},
|
||||||
/* Mares Icon HD */
|
/* Mares Icon HD */
|
||||||
{"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Matrix", DC_FAMILY_MARES_ICONHD , 0x0F, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Smart", DC_FAMILY_MARES_ICONHD , 0x000010, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||||
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Smart Apnea", DC_FAMILY_MARES_ICONHD , 0x010010, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||||
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||||
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
|
||||||
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL, NULL},
|
{"Mares", "Quad Air", DC_FAMILY_MARES_ICONHD , 0x23, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||||
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL, NULL},
|
{"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, NULL},
|
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
|
||||||
/* Heinrichs Weikamp */
|
/* Heinrichs Weikamp */
|
||||||
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
|
{"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},
|
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
|
||||||
@ -502,6 +503,19 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dc_filter_mares (dc_transport_t transport, const void *userdata)
|
||||||
|
{
|
||||||
|
static const char *bluetooth[] = {
|
||||||
|
"Mares bluelink pro",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (transport == DC_TRANSPORT_BLE) {
|
||||||
|
return dc_filter_internal_name ((const char *) userdata, bluetooth, C_ARRAY_SIZE(bluetooth));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
dc_status_t
|
dc_status_t
|
||||||
dc_descriptor_iterator (dc_iterator_t **out)
|
dc_descriptor_iterator (dc_iterator_t **out)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -45,6 +45,8 @@
|
|||||||
#define SMARTAIR 0x24
|
#define SMARTAIR 0x24
|
||||||
#define QUAD 0x29
|
#define QUAD 0x29
|
||||||
|
|
||||||
|
#define MAXRETRIES 4
|
||||||
|
|
||||||
#define ACK 0xAA
|
#define ACK 0xAA
|
||||||
#define EOF 0xEA
|
#define EOF 0xEA
|
||||||
|
|
||||||
@ -72,6 +74,9 @@ typedef struct mares_iconhd_device_t {
|
|||||||
unsigned char version[140];
|
unsigned char version[140];
|
||||||
unsigned int model;
|
unsigned int model;
|
||||||
unsigned int packetsize;
|
unsigned int packetsize;
|
||||||
|
unsigned char cache[20];
|
||||||
|
unsigned int available;
|
||||||
|
unsigned int offset;
|
||||||
} mares_iconhd_device_t;
|
} 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_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
|
||||||
@ -146,7 +151,79 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static dc_status_t
|
static dc_status_t
|
||||||
mares_iconhd_transfer (mares_iconhd_device_t *device,
|
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,
|
const unsigned char command[], unsigned int csize,
|
||||||
unsigned char answer[], unsigned int asize)
|
unsigned char answer[], unsigned int asize)
|
||||||
{
|
{
|
||||||
@ -159,7 +236,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
|||||||
return DC_STATUS_CANCELLED;
|
return DC_STATUS_CANCELLED;
|
||||||
|
|
||||||
// Send the command header to the dive computer.
|
// Send the command header to the dive computer.
|
||||||
status = dc_iostream_write (device->iostream, command, 2, NULL);
|
status = mares_iconhd_write (device, command, 2);
|
||||||
if (status != DC_STATUS_SUCCESS) {
|
if (status != DC_STATUS_SUCCESS) {
|
||||||
ERROR (abstract->context, "Failed to send the command.");
|
ERROR (abstract->context, "Failed to send the command.");
|
||||||
return status;
|
return status;
|
||||||
@ -167,7 +244,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
|||||||
|
|
||||||
// Receive the header byte.
|
// Receive the header byte.
|
||||||
unsigned char header[1] = {0};
|
unsigned char header[1] = {0};
|
||||||
status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
|
status = mares_iconhd_read (device, header, sizeof (header));
|
||||||
if (status != DC_STATUS_SUCCESS) {
|
if (status != DC_STATUS_SUCCESS) {
|
||||||
ERROR (abstract->context, "Failed to receive the answer.");
|
ERROR (abstract->context, "Failed to receive the answer.");
|
||||||
return status;
|
return status;
|
||||||
@ -181,7 +258,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
|||||||
|
|
||||||
// Send the command payload to the dive computer.
|
// Send the command payload to the dive computer.
|
||||||
if (csize > 2) {
|
if (csize > 2) {
|
||||||
status = dc_iostream_write (device->iostream, command + 2, csize - 2, NULL);
|
status = mares_iconhd_write (device, command + 2, csize - 2);
|
||||||
if (status != DC_STATUS_SUCCESS) {
|
if (status != DC_STATUS_SUCCESS) {
|
||||||
ERROR (abstract->context, "Failed to send the command.");
|
ERROR (abstract->context, "Failed to send the command.");
|
||||||
return status;
|
return status;
|
||||||
@ -189,7 +266,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the packet.
|
// Read the packet.
|
||||||
status = dc_iostream_read (device->iostream, answer, asize, NULL);
|
status = mares_iconhd_read (device, answer, asize);
|
||||||
if (status != DC_STATUS_SUCCESS) {
|
if (status != DC_STATUS_SUCCESS) {
|
||||||
ERROR (abstract->context, "Failed to receive the answer.");
|
ERROR (abstract->context, "Failed to receive the answer.");
|
||||||
return status;
|
return status;
|
||||||
@ -197,7 +274,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
|||||||
|
|
||||||
// Receive the trailer byte.
|
// Receive the trailer byte.
|
||||||
unsigned char trailer[1] = {0};
|
unsigned char trailer[1] = {0};
|
||||||
status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
|
status = mares_iconhd_read (device, trailer, sizeof (trailer));
|
||||||
if (status != DC_STATUS_SUCCESS) {
|
if (status != DC_STATUS_SUCCESS) {
|
||||||
ERROR (abstract->context, "Failed to receive the answer.");
|
ERROR (abstract->context, "Failed to receive the answer.");
|
||||||
return status;
|
return status;
|
||||||
@ -212,6 +289,27 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
|
|||||||
return DC_STATUS_SUCCESS;
|
return DC_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dc_status_t
|
||||||
|
mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
|
||||||
|
{
|
||||||
|
unsigned int nretries = 0;
|
||||||
|
dc_status_t rc = DC_STATUS_SUCCESS;
|
||||||
|
while ((rc = mares_iconhd_packet (device, command, csize, answer, asize)) != DC_STATUS_SUCCESS) {
|
||||||
|
// Automatically discard a corrupted packet,
|
||||||
|
// and request a new one.
|
||||||
|
if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// Abort if the maximum number of retries is reached.
|
||||||
|
if (nretries++ >= MAXRETRIES)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// Discard any garbage bytes.
|
||||||
|
dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DC_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
dc_status_t
|
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)
|
||||||
@ -236,6 +334,9 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
|
|||||||
memset (device->version, 0, sizeof (device->version));
|
memset (device->version, 0, sizeof (device->version));
|
||||||
device->model = 0;
|
device->model = 0;
|
||||||
device->packetsize = 0;
|
device->packetsize = 0;
|
||||||
|
memset (device->cache, 0, sizeof (device->cache));
|
||||||
|
device->available = 0;
|
||||||
|
device->offset = 0;
|
||||||
|
|
||||||
// Set the serial communication protocol (115200 8E1).
|
// Set the serial communication protocol (115200 8E1).
|
||||||
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
|
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user